1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/search/content/search.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,841 @@ 1.4 +<?xml version="1.0"?> 1.5 +# -*- Mode: HTML -*- 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +<!DOCTYPE bindings [ 1.11 +<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" > 1.12 +%searchBarDTD; 1.13 +<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> 1.14 +%browserDTD; 1.15 +]> 1.16 + 1.17 +<bindings id="SearchBindings" 1.18 + xmlns="http://www.mozilla.org/xbl" 1.19 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 1.20 + xmlns:xbl="http://www.mozilla.org/xbl"> 1.21 + 1.22 + <binding id="searchbar"> 1.23 + <resources> 1.24 + <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/> 1.25 + <stylesheet src="chrome://browser/skin/searchbar.css"/> 1.26 + </resources> 1.27 + <content> 1.28 + <xul:stringbundle src="chrome://browser/locale/search.properties" 1.29 + anonid="searchbar-stringbundle"/> 1.30 + <!-- 1.31 + There is a dependency between "maxrows" attribute and 1.32 + "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing 1.33 + one of them requires changing the other one. 1.34 + --> 1.35 + <xul:textbox class="searchbar-textbox" 1.36 + anonid="searchbar-textbox" 1.37 + type="autocomplete" 1.38 + flex="1" 1.39 + autocompletepopup="PopupAutoComplete" 1.40 + autocompletesearch="search-autocomplete" 1.41 + autocompletesearchparam="searchbar-history" 1.42 + timeout="250" 1.43 + maxrows="10" 1.44 + completeselectedindex="true" 1.45 + showcommentcolumn="true" 1.46 + tabscrolling="true" 1.47 + xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines"> 1.48 + <!-- 1.49 + Empty <box> to properly position the icon within the autocomplete 1.50 + binding's anonymous children (the autocomplete binding positions <box> 1.51 + children differently) 1.52 + --> 1.53 + <xul:box> 1.54 + <xul:button class="searchbar-engine-button" 1.55 + type="menu" 1.56 + anonid="searchbar-engine-button"> 1.57 + <xul:image class="searchbar-engine-image" xbl:inherits="src"/> 1.58 + <xul:image class="searchbar-dropmarker-image"/> 1.59 + <xul:menupopup class="searchbar-popup" 1.60 + anonid="searchbar-popup"> 1.61 + <xul:menuseparator/> 1.62 + <xul:menuitem class="open-engine-manager" 1.63 + anonid="open-engine-manager" 1.64 + label="&cmd_engineManager.label;" 1.65 + oncommand="openManager(event);"/> 1.66 + </xul:menupopup> 1.67 + </xul:button> 1.68 + </xul:box> 1.69 + <xul:hbox class="search-go-container"> 1.70 + <xul:image class="search-go-button" 1.71 + anonid="search-go-button" 1.72 + onclick="handleSearchCommand(event);" 1.73 + tooltiptext="&searchEndCap.label;"/> 1.74 + </xul:hbox> 1.75 + </xul:textbox> 1.76 + </content> 1.77 + 1.78 + <implementation implements="nsIObserver"> 1.79 + <constructor><![CDATA[ 1.80 + if (this.parentNode.parentNode.localName == "toolbarpaletteitem") 1.81 + return; 1.82 + // Make sure we rebuild the popup in onpopupshowing 1.83 + this._needToBuildPopup = true; 1.84 + 1.85 + var os = 1.86 + Components.classes["@mozilla.org/observer-service;1"] 1.87 + .getService(Components.interfaces.nsIObserverService); 1.88 + os.addObserver(this, "browser-search-engine-modified", false); 1.89 + 1.90 + this._initialized = true; 1.91 + 1.92 + this.searchService.init((function search_init_cb(aStatus) { 1.93 + // Bail out if the binding's been destroyed 1.94 + if (!this._initialized) 1.95 + return; 1.96 + 1.97 + if (Components.isSuccessCode(aStatus)) { 1.98 + // Refresh the display (updating icon, etc) 1.99 + this.updateDisplay(); 1.100 + } else { 1.101 + Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus); 1.102 + } 1.103 + }).bind(this)); 1.104 + ]]></constructor> 1.105 + 1.106 + <destructor><![CDATA[ 1.107 + if (this._initialized) { 1.108 + this._initialized = false; 1.109 + 1.110 + var os = Components.classes["@mozilla.org/observer-service;1"] 1.111 + .getService(Components.interfaces.nsIObserverService); 1.112 + os.removeObserver(this, "browser-search-engine-modified"); 1.113 + } 1.114 + 1.115 + // Make sure to break the cycle from _textbox to us. Otherwise we leak 1.116 + // the world. But make sure it's actually pointing to us. 1.117 + if (this._textbox.mController.input == this) 1.118 + this._textbox.mController.input = null; 1.119 + ]]></destructor> 1.120 + 1.121 + <field name="_stringBundle">document.getAnonymousElementByAttribute(this, 1.122 + "anonid", "searchbar-stringbundle");</field> 1.123 + <field name="_textbox">document.getAnonymousElementByAttribute(this, 1.124 + "anonid", "searchbar-textbox");</field> 1.125 + <field name="_popup">document.getAnonymousElementByAttribute(this, 1.126 + "anonid", "searchbar-popup");</field> 1.127 + <field name="_ss">null</field> 1.128 + <field name="_engines">null</field> 1.129 + <field name="FormHistory" readonly="true"> 1.130 + (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory; 1.131 + </field> 1.132 + 1.133 + <property name="engines" readonly="true"> 1.134 + <getter><![CDATA[ 1.135 + if (!this._engines) 1.136 + this._engines = this.searchService.getVisibleEngines(); 1.137 + return this._engines; 1.138 + ]]></getter> 1.139 + </property> 1.140 + 1.141 + <field name="searchButton">document.getAnonymousElementByAttribute(this, 1.142 + "anonid", "searchbar-engine-button");</field> 1.143 + 1.144 + <property name="currentEngine"> 1.145 + <setter><![CDATA[ 1.146 + let ss = this.searchService; 1.147 + ss.defaultEngine = ss.currentEngine = val; 1.148 + return val; 1.149 + ]]></setter> 1.150 + <getter><![CDATA[ 1.151 + var currentEngine = this.searchService.currentEngine; 1.152 + // Return a dummy engine if there is no currentEngine 1.153 + return currentEngine || {name: "", uri: null}; 1.154 + ]]></getter> 1.155 + </property> 1.156 + 1.157 + <!-- textbox is used by sanitize.js to clear the undo history when 1.158 + clearing form information. --> 1.159 + <property name="textbox" readonly="true" 1.160 + onget="return this._textbox;"/> 1.161 + 1.162 + <property name="searchService" readonly="true"> 1.163 + <getter><![CDATA[ 1.164 + if (!this._ss) { 1.165 + const nsIBSS = Components.interfaces.nsIBrowserSearchService; 1.166 + this._ss = 1.167 + Components.classes["@mozilla.org/browser/search-service;1"] 1.168 + .getService(nsIBSS); 1.169 + } 1.170 + return this._ss; 1.171 + ]]></getter> 1.172 + </property> 1.173 + 1.174 + <property name="value" onget="return this._textbox.value;" 1.175 + onset="return this._textbox.value = val;"/> 1.176 + 1.177 + <method name="focus"> 1.178 + <body><![CDATA[ 1.179 + this._textbox.focus(); 1.180 + ]]></body> 1.181 + </method> 1.182 + 1.183 + <method name="select"> 1.184 + <body><![CDATA[ 1.185 + this._textbox.select(); 1.186 + ]]></body> 1.187 + </method> 1.188 + 1.189 + <method name="observe"> 1.190 + <parameter name="aEngine"/> 1.191 + <parameter name="aTopic"/> 1.192 + <parameter name="aVerb"/> 1.193 + <body><![CDATA[ 1.194 + if (aTopic == "browser-search-engine-modified") { 1.195 + switch (aVerb) { 1.196 + case "engine-removed": 1.197 + this.offerNewEngine(aEngine); 1.198 + break; 1.199 + case "engine-added": 1.200 + this.hideNewEngine(aEngine); 1.201 + break; 1.202 + case "engine-current": 1.203 + // The current engine was changed. Rebuilding the menu appears to 1.204 + // confuse its idea of whether it should be open when it's just 1.205 + // been clicked, so we force it to close now. 1.206 + this._popup.hidePopup(); 1.207 + break; 1.208 + case "engine-changed": 1.209 + // An engine was removed (or hidden) or added, or an icon was 1.210 + // changed. Do nothing special. 1.211 + } 1.212 + 1.213 + // Make sure the engine list is refetched next time it's needed 1.214 + this._engines = null; 1.215 + 1.216 + // Rebuild the popup and update the display after any modification. 1.217 + this.rebuildPopup(); 1.218 + this.updateDisplay(); 1.219 + } 1.220 + ]]></body> 1.221 + </method> 1.222 + 1.223 + <!-- There are two seaprate lists of search engines, whose uses intersect 1.224 + in this file. The search service (nsIBrowserSearchService and 1.225 + nsSearchService.js) maintains a list of Engine objects which is used to 1.226 + populate the searchbox list of available engines and to perform queries. 1.227 + That list is accessed here via this.SearchService, and it's that sort of 1.228 + Engine that is passed to this binding's observer as aEngine. 1.229 + 1.230 + In addition, browser.js fills two lists of autodetected search engines 1.231 + (browser.engines and browser.hiddenEngines) as properties of 1.232 + mCurrentBrowser. Those lists contain unnamed JS objects of the form 1.233 + { uri:, title:, icon: }, and that's what the searchbar uses to determine 1.234 + whether to show any "Add <EngineName>" menu items in the drop-down. 1.235 + 1.236 + The two types of engines are currently related by their identifying 1.237 + titles (the Engine object's 'name'), although that may change; see bug 1.238 + 335102. --> 1.239 + 1.240 + <!-- If the engine that was just removed from the searchbox list was 1.241 + autodetected on this page, move it to each browser's active list so it 1.242 + will be offered to be added again. --> 1.243 + <method name="offerNewEngine"> 1.244 + <parameter name="aEngine"/> 1.245 + <body><![CDATA[ 1.246 + var allbrowsers = getBrowser().mPanelContainer.childNodes; 1.247 + for (var tab = 0; tab < allbrowsers.length; tab++) { 1.248 + var browser = getBrowser().getBrowserAtIndex(tab); 1.249 + if (browser.hiddenEngines) { 1.250 + // XXX This will need to be changed when engines are identified by 1.251 + // URL rather than title; see bug 335102. 1.252 + var removeTitle = aEngine.wrappedJSObject.name; 1.253 + for (var i = 0; i < browser.hiddenEngines.length; i++) { 1.254 + if (browser.hiddenEngines[i].title == removeTitle) { 1.255 + if (!browser.engines) 1.256 + browser.engines = []; 1.257 + browser.engines.push(browser.hiddenEngines[i]); 1.258 + browser.hiddenEngines.splice(i, 1); 1.259 + break; 1.260 + } 1.261 + } 1.262 + } 1.263 + } 1.264 + ]]></body> 1.265 + </method> 1.266 + 1.267 + <!-- If the engine that was just added to the searchbox list was 1.268 + autodetected on this page, move it to each browser's hidden list so it is 1.269 + no longer offered to be added. --> 1.270 + <method name="hideNewEngine"> 1.271 + <parameter name="aEngine"/> 1.272 + <body><![CDATA[ 1.273 + var allbrowsers = getBrowser().mPanelContainer.childNodes; 1.274 + for (var tab = 0; tab < allbrowsers.length; tab++) { 1.275 + var browser = getBrowser().getBrowserAtIndex(tab); 1.276 + if (browser.engines) { 1.277 + // XXX This will need to be changed when engines are identified by 1.278 + // URL rather than title; see bug 335102. 1.279 + var removeTitle = aEngine.wrappedJSObject.name; 1.280 + for (var i = 0; i < browser.engines.length; i++) { 1.281 + if (browser.engines[i].title == removeTitle) { 1.282 + if (!browser.hiddenEngines) 1.283 + browser.hiddenEngines = []; 1.284 + browser.hiddenEngines.push(browser.engines[i]); 1.285 + browser.engines.splice(i, 1); 1.286 + break; 1.287 + } 1.288 + } 1.289 + } 1.290 + } 1.291 + ]]></body> 1.292 + </method> 1.293 + 1.294 + <method name="setIcon"> 1.295 + <parameter name="element"/> 1.296 + <parameter name="uri"/> 1.297 + <body><![CDATA[ 1.298 + if (uri) { 1.299 + let size = Math.round(16 * window.devicePixelRatio); 1.300 + if (!uri.contains("#")) 1.301 + uri += "#-moz-resolution=" + size + "," + size; 1.302 + } 1.303 + element.setAttribute("src", uri); 1.304 + ]]></body> 1.305 + </method> 1.306 + 1.307 + <method name="updateDisplay"> 1.308 + <body><![CDATA[ 1.309 + var uri = this.currentEngine.iconURI; 1.310 + this.setIcon(this, uri ? uri.spec : ""); 1.311 + 1.312 + var name = this.currentEngine.name; 1.313 + var text = this._stringBundle.getFormattedString("searchtip", [name]); 1.314 + this._textbox.placeholder = name; 1.315 + this._textbox.label = text; 1.316 + this._textbox.tooltipText = text; 1.317 + ]]></body> 1.318 + </method> 1.319 + 1.320 + <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items 1.321 + for new search engines that can be added to the available list). This 1.322 + is called each time the popup is shown. 1.323 + --> 1.324 + <method name="rebuildPopupDynamic"> 1.325 + <body><![CDATA[ 1.326 + // We might not have added the main popup items yet, do that first 1.327 + // if needed. 1.328 + if (this._needToBuildPopup) 1.329 + this.rebuildPopup(); 1.330 + 1.331 + var popup = this._popup; 1.332 + // Clear any addengine menuitems, including addengine-item entries and 1.333 + // the addengine-separator. Work backward to avoid invalidating the 1.334 + // indexes as items are removed. 1.335 + var items = popup.childNodes; 1.336 + for (var i = items.length - 1; i >= 0; i--) { 1.337 + if (items[i].classList.contains("addengine-item") || 1.338 + items[i].classList.contains("addengine-separator")) 1.339 + popup.removeChild(items[i]); 1.340 + } 1.341 + 1.342 + var addengines = getBrowser().mCurrentBrowser.engines; 1.343 + if (addengines && addengines.length > 0) { 1.344 + const kXULNS = 1.345 + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.346 + 1.347 + // Find the (first) separator in the remaining menu, or the first item 1.348 + // if no separators are present. 1.349 + var insertLocation = popup.firstChild; 1.350 + while (insertLocation.nextSibling && 1.351 + insertLocation.localName != "menuseparator") { 1.352 + insertLocation = insertLocation.nextSibling; 1.353 + } 1.354 + if (insertLocation.localName != "menuseparator") 1.355 + insertLocation = popup.firstChild; 1.356 + 1.357 + var separator = document.createElementNS(kXULNS, "menuseparator"); 1.358 + separator.setAttribute("class", "addengine-separator"); 1.359 + popup.insertBefore(separator, insertLocation); 1.360 + 1.361 + // Insert the "add this engine" items. 1.362 + for (var i = 0; i < addengines.length; i++) { 1.363 + var menuitem = document.createElement("menuitem"); 1.364 + var engineInfo = addengines[i]; 1.365 + var labelStr = 1.366 + this._stringBundle.getFormattedString("cmd_addFoundEngine", 1.367 + [engineInfo.title]); 1.368 + menuitem = document.createElementNS(kXULNS, "menuitem"); 1.369 + menuitem.setAttribute("class", "menuitem-iconic addengine-item"); 1.370 + menuitem.setAttribute("label", labelStr); 1.371 + menuitem.setAttribute("tooltiptext", engineInfo.uri); 1.372 + menuitem.setAttribute("uri", engineInfo.uri); 1.373 + if (engineInfo.icon) 1.374 + this.setIcon(menuitem, engineInfo.icon); 1.375 + menuitem.setAttribute("title", engineInfo.title); 1.376 + popup.insertBefore(menuitem, insertLocation); 1.377 + } 1.378 + } 1.379 + ]]></body> 1.380 + </method> 1.381 + 1.382 + <!-- Rebuilds the list of visible search engines in the menu. Does not remove 1.383 + or update any dynamic entries (i.e., "Add this engine" items) nor the 1.384 + Manage Engines item. This is called by the observer when the list of 1.385 + visible engines, or the currently selected engine, has changed. 1.386 + --> 1.387 + <method name="rebuildPopup"> 1.388 + <body><![CDATA[ 1.389 + var popup = this._popup; 1.390 + 1.391 + // Clear the popup, down to the first separator 1.392 + while (popup.firstChild && popup.firstChild.localName != "menuseparator") 1.393 + popup.removeChild(popup.firstChild); 1.394 + 1.395 + const kXULNS = 1.396 + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.397 + 1.398 + var engines = this.engines; 1.399 + for (var i = engines.length - 1; i >= 0; --i) { 1.400 + var menuitem = document.createElementNS(kXULNS, "menuitem"); 1.401 + var name = engines[i].name; 1.402 + menuitem.setAttribute("label", name); 1.403 + menuitem.setAttribute("id", name); 1.404 + menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon"); 1.405 + // Since this menu is rebuilt by the observer method whenever a new 1.406 + // engine is selected, the "selected" attribute does not need to be 1.407 + // explicitly cleared anywhere. 1.408 + if (engines[i] == this.currentEngine) 1.409 + menuitem.setAttribute("selected", "true"); 1.410 + var tooltip = this._stringBundle.getFormattedString("searchtip", [name]); 1.411 + menuitem.setAttribute("tooltiptext", tooltip); 1.412 + if (engines[i].iconURI) 1.413 + this.setIcon(menuitem, engines[i].iconURI.spec); 1.414 + popup.insertBefore(menuitem, popup.firstChild); 1.415 + menuitem.engine = engines[i]; 1.416 + } 1.417 + 1.418 + this._needToBuildPopup = false; 1.419 + ]]></body> 1.420 + </method> 1.421 + 1.422 + <method name="openManager"> 1.423 + <parameter name="aEvent"/> 1.424 + <body><![CDATA[ 1.425 + var wm = 1.426 + Components.classes["@mozilla.org/appshell/window-mediator;1"] 1.427 + .getService(Components.interfaces.nsIWindowMediator); 1.428 + 1.429 + var window = wm.getMostRecentWindow("Browser:SearchManager"); 1.430 + if (window) 1.431 + window.focus() 1.432 + else { 1.433 + setTimeout(function () { 1.434 + openDialog("chrome://browser/content/search/engineManager.xul", 1.435 + "_blank", "chrome,dialog,modal,centerscreen,resizable"); 1.436 + }, 0); 1.437 + } 1.438 + ]]></body> 1.439 + </method> 1.440 + 1.441 + <method name="selectEngine"> 1.442 + <parameter name="aEvent"/> 1.443 + <parameter name="isNextEngine"/> 1.444 + <body><![CDATA[ 1.445 + // Find the new index 1.446 + var newIndex = this.engines.indexOf(this.currentEngine); 1.447 + newIndex += isNextEngine ? 1 : -1; 1.448 + 1.449 + if (newIndex >= 0 && newIndex < this.engines.length) { 1.450 + this.currentEngine = this.engines[newIndex]; 1.451 + } 1.452 + 1.453 + aEvent.preventDefault(); 1.454 + aEvent.stopPropagation(); 1.455 + ]]></body> 1.456 + </method> 1.457 + 1.458 + <method name="handleSearchCommand"> 1.459 + <parameter name="aEvent"/> 1.460 + <body><![CDATA[ 1.461 + var textBox = this._textbox; 1.462 + var textValue = textBox.value; 1.463 + 1.464 + var where = "current"; 1.465 + if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") { 1.466 + if (aEvent.button == 2) 1.467 + return; 1.468 + where = whereToOpenLink(aEvent, false, true); 1.469 + } 1.470 + else { 1.471 + var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab"); 1.472 + if ((aEvent && aEvent.altKey) ^ newTabPref) 1.473 + where = "tab"; 1.474 + } 1.475 + 1.476 + this.doSearch(textValue, where); 1.477 + ]]></body> 1.478 + </method> 1.479 + 1.480 + <method name="doSearch"> 1.481 + <parameter name="aData"/> 1.482 + <parameter name="aWhere"/> 1.483 + <body><![CDATA[ 1.484 + var textBox = this._textbox; 1.485 + 1.486 + // Save the current value in the form history 1.487 + if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) { 1.488 + this.FormHistory.update( 1.489 + { op : "bump", 1.490 + fieldname : textBox.getAttribute("autocompletesearchparam"), 1.491 + value : aData }, 1.492 + { handleError : function(aError) { 1.493 + Components.utils.reportError("Saving search to form history failed: " + aError.message); 1.494 + }}); 1.495 + } 1.496 + 1.497 + // null parameter below specifies HTML response for search 1.498 + var submission = this.currentEngine.getSubmission(aData, null, "searchbar"); 1.499 + BrowserSearch.recordSearchInHealthReport(this.currentEngine, "searchbar"); 1.500 + openUILinkIn(submission.uri.spec, aWhere, null, submission.postData); 1.501 + ]]></body> 1.502 + </method> 1.503 + </implementation> 1.504 + 1.505 + <handlers> 1.506 + <handler event="command"><![CDATA[ 1.507 + const target = event.originalTarget; 1.508 + if (target.engine) { 1.509 + this.currentEngine = target.engine; 1.510 + } else if (target.classList.contains("addengine-item")) { 1.511 + var searchService = 1.512 + Components.classes["@mozilla.org/browser/search-service;1"] 1.513 + .getService(Components.interfaces.nsIBrowserSearchService); 1.514 + // We only detect OpenSearch files 1.515 + var type = Components.interfaces.nsISearchEngine.DATA_XML; 1.516 + // Select the installed engine if the installation succeeds 1.517 + var installCallback = { 1.518 + onSuccess: engine => this.currentEngine = engine 1.519 + } 1.520 + searchService.addEngine(target.getAttribute("uri"), type, 1.521 + target.getAttribute("src"), false, 1.522 + installCallback); 1.523 + } 1.524 + else 1.525 + return; 1.526 + 1.527 + this.focus(); 1.528 + this.select(); 1.529 + ]]></handler> 1.530 + 1.531 + <handler event="popupshowing" action="this.rebuildPopupDynamic();"/> 1.532 + 1.533 + <handler event="DOMMouseScroll" 1.534 + phase="capturing" 1.535 + modifiers="accel" 1.536 + action="this.selectEngine(event, (event.detail > 0));"/> 1.537 + 1.538 + <handler event="focus"> 1.539 + <![CDATA[ 1.540 + // Speculatively connect to the current engine's search URI (and 1.541 + // suggest URI, if different) to reduce request latency 1.542 + 1.543 + const SUGGEST_TYPE = "application/x-suggestions+json"; 1.544 + var engine = this.currentEngine; 1.545 + var connector = 1.546 + Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect); 1.547 + var searchURI = engine.getSubmission("dummy", null, "searchbar").uri; 1.548 + let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) 1.549 + .getInterface(Components.interfaces.nsIWebNavigation) 1.550 + .QueryInterface(Components.interfaces.nsILoadContext); 1.551 + connector.speculativeConnect(searchURI, callbacks); 1.552 + 1.553 + if (engine.supportsResponseType(SUGGEST_TYPE)) { 1.554 + var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE, "searchbar").uri; 1.555 + if (suggestURI.prePath != searchURI.prePath) 1.556 + connector.speculativeConnect(suggestURI, callbacks); 1.557 + } 1.558 + ]]></handler> 1.559 + </handlers> 1.560 + </binding> 1.561 + 1.562 + <binding id="searchbar-textbox" 1.563 + extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> 1.564 + <implementation implements="nsIObserver"> 1.565 + <constructor><![CDATA[ 1.566 + const kXULNS = 1.567 + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.568 + 1.569 + if (document.getBindingParent(this).parentNode.parentNode.localName == 1.570 + "toolbarpaletteitem") 1.571 + return; 1.572 + 1.573 + // Initialize fields 1.574 + this._stringBundle = document.getBindingParent(this)._stringBundle; 1.575 + this._prefBranch = 1.576 + Components.classes["@mozilla.org/preferences-service;1"] 1.577 + .getService(Components.interfaces.nsIPrefBranch); 1.578 + this._suggestEnabled = 1.579 + this._prefBranch.getBoolPref("browser.search.suggest.enabled"); 1.580 + 1.581 + if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll")) 1.582 + this.setAttribute("clickSelectsAll", true); 1.583 + 1.584 + // Add items to context menu and attach controller to handle them 1.585 + var textBox = document.getAnonymousElementByAttribute(this, 1.586 + "anonid", "textbox-input-box"); 1.587 + var cxmenu = document.getAnonymousElementByAttribute(textBox, 1.588 + "anonid", "input-box-contextmenu"); 1.589 + var pasteAndSearch; 1.590 + cxmenu.addEventListener("popupshowing", function() { 1.591 + if (!pasteAndSearch) 1.592 + return; 1.593 + var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); 1.594 + var enabled = controller.isCommandEnabled("cmd_paste"); 1.595 + if (enabled) 1.596 + pasteAndSearch.removeAttribute("disabled"); 1.597 + else 1.598 + pasteAndSearch.setAttribute("disabled", "true"); 1.599 + }, false); 1.600 + 1.601 + var element, label, akey; 1.602 + 1.603 + element = document.createElementNS(kXULNS, "menuseparator"); 1.604 + cxmenu.appendChild(element); 1.605 + 1.606 + var insertLocation = cxmenu.firstChild; 1.607 + while (insertLocation.nextSibling && 1.608 + insertLocation.getAttribute("cmd") != "cmd_paste") 1.609 + insertLocation = insertLocation.nextSibling; 1.610 + if (insertLocation) { 1.611 + element = document.createElementNS(kXULNS, "menuitem"); 1.612 + label = this._stringBundle.getString("cmd_pasteAndSearch"); 1.613 + element.setAttribute("label", label); 1.614 + element.setAttribute("anonid", "paste-and-search"); 1.615 + element.setAttribute("oncommand", 1.616 + "BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();"); 1.617 + cxmenu.insertBefore(element, insertLocation.nextSibling); 1.618 + pasteAndSearch = element; 1.619 + } 1.620 + 1.621 + element = document.createElementNS(kXULNS, "menuitem"); 1.622 + label = this._stringBundle.getString("cmd_clearHistory"); 1.623 + akey = this._stringBundle.getString("cmd_clearHistory_accesskey"); 1.624 + element.setAttribute("label", label); 1.625 + element.setAttribute("accesskey", akey); 1.626 + element.setAttribute("cmd", "cmd_clearhistory"); 1.627 + cxmenu.appendChild(element); 1.628 + 1.629 + element = document.createElementNS(kXULNS, "menuitem"); 1.630 + label = this._stringBundle.getString("cmd_showSuggestions"); 1.631 + akey = this._stringBundle.getString("cmd_showSuggestions_accesskey"); 1.632 + element.setAttribute("anonid", "toggle-suggest-item"); 1.633 + element.setAttribute("label", label); 1.634 + element.setAttribute("accesskey", akey); 1.635 + element.setAttribute("cmd", "cmd_togglesuggest"); 1.636 + element.setAttribute("type", "checkbox"); 1.637 + element.setAttribute("checked", this._suggestEnabled); 1.638 + element.setAttribute("autocheck", "false"); 1.639 + this._suggestMenuItem = element; 1.640 + cxmenu.appendChild(element); 1.641 + 1.642 + this.controllers.appendController(this.searchbarController); 1.643 + 1.644 + // Add observer for suggest preference 1.645 + var prefs = Components.classes["@mozilla.org/preferences-service;1"] 1.646 + .getService(Components.interfaces.nsIPrefBranch); 1.647 + prefs.addObserver("browser.search.suggest.enabled", this, false); 1.648 + ]]></constructor> 1.649 + 1.650 + <destructor><![CDATA[ 1.651 + var prefs = Components.classes["@mozilla.org/preferences-service;1"] 1.652 + .getService(Components.interfaces.nsIPrefBranch); 1.653 + prefs.removeObserver("browser.search.suggest.enabled", this); 1.654 + 1.655 + // Because XBL and the customize toolbar code interacts poorly, 1.656 + // there may not be anything to remove here 1.657 + try { 1.658 + this.controllers.removeController(this.searchbarController); 1.659 + } catch (ex) { } 1.660 + ]]></destructor> 1.661 + 1.662 + <field name="_stringBundle"/> 1.663 + <field name="_prefBranch"/> 1.664 + <field name="_suggestMenuItem"/> 1.665 + <field name="_suggestEnabled"/> 1.666 + 1.667 + <!-- 1.668 + This overrides the searchParam property in autocomplete.xml. We're 1.669 + hijacking this property as a vehicle for delivering the privacy 1.670 + information about the window into the guts of nsSearchSuggestions. 1.671 + 1.672 + Note that the setter is the same as the parent. We were not sure whether 1.673 + we can override just the getter. If that proves to be the case, the setter 1.674 + can be removed. 1.675 + --> 1.676 + <property name="searchParam" 1.677 + onget="return this.getAttribute('autocompletesearchparam') + 1.678 + (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');" 1.679 + onset="this.setAttribute('autocompletesearchparam', val); return val;"/> 1.680 + 1.681 + <!-- 1.682 + This method overrides the autocomplete binding's openPopup (essentially 1.683 + duplicating the logic from the autocomplete popup binding's 1.684 + openAutocompletePopup method), modifying it so that the popup is aligned with 1.685 + the inner textbox, but sized to not extend beyond the search bar border. 1.686 + --> 1.687 + <method name="openPopup"> 1.688 + <body><![CDATA[ 1.689 + var popup = this.popup; 1.690 + if (!popup.mPopupOpen) { 1.691 + // Initially the panel used for the searchbar (PopupAutoComplete 1.692 + // in browser.xul) is hidden to avoid impacting startup / new 1.693 + // window performance. The base binding's openPopup would normally 1.694 + // call the overriden openAutocompletePopup in urlbarBindings.xml's 1.695 + // browser-autocomplete-result-popup binding to unhide the popup, 1.696 + // but since we're overriding openPopup we need to unhide the panel 1.697 + // ourselves. 1.698 + popup.hidden = false; 1.699 + 1.700 + popup.mInput = this; 1.701 + popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView); 1.702 + popup.invalidate(); 1.703 + 1.704 + popup.showCommentColumn = this.showCommentColumn; 1.705 + popup.showImageColumn = this.showImageColumn; 1.706 + 1.707 + document.popupNode = null; 1.708 + 1.709 + const isRTL = getComputedStyle(this, "").direction == "rtl"; 1.710 + 1.711 + var outerRect = this.getBoundingClientRect(); 1.712 + var innerRect = this.inputField.getBoundingClientRect(); 1.713 + if (isRTL) { 1.714 + var width = innerRect.right - outerRect.left; 1.715 + } else { 1.716 + var width = outerRect.right - innerRect.left; 1.717 + } 1.718 + popup.setAttribute("width", width > 100 ? width : 100); 1.719 + 1.720 + var yOffset = outerRect.bottom - innerRect.bottom; 1.721 + popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false); 1.722 + } 1.723 + ]]></body> 1.724 + </method> 1.725 + 1.726 + <method name="observe"> 1.727 + <parameter name="aSubject"/> 1.728 + <parameter name="aTopic"/> 1.729 + <parameter name="aData"/> 1.730 + <body><![CDATA[ 1.731 + if (aTopic == "nsPref:changed") { 1.732 + this._suggestEnabled = 1.733 + this._prefBranch.getBoolPref("browser.search.suggest.enabled"); 1.734 + this._suggestMenuItem.setAttribute("checked", this._suggestEnabled); 1.735 + } 1.736 + ]]></body> 1.737 + </method> 1.738 + 1.739 + <method name="openSearch"> 1.740 + <body> 1.741 + <![CDATA[ 1.742 + // Don't open search popup if history popup is open 1.743 + if (!this.popupOpen) { 1.744 + document.getBindingParent(this).searchButton.open = true; 1.745 + return false; 1.746 + } 1.747 + return true; 1.748 + ]]> 1.749 + </body> 1.750 + </method> 1.751 + 1.752 + <!-- override |onTextEntered| in autocomplete.xml --> 1.753 + <method name="onTextEntered"> 1.754 + <parameter name="aEvent"/> 1.755 + <body><![CDATA[ 1.756 + var evt = aEvent || this.mEnterEvent; 1.757 + document.getBindingParent(this).handleSearchCommand(evt); 1.758 + this.mEnterEvent = null; 1.759 + ]]></body> 1.760 + </method> 1.761 + 1.762 + <!-- nsIController --> 1.763 + <field name="searchbarController" readonly="true"><![CDATA[({ 1.764 + _self: this, 1.765 + supportsCommand: function(aCommand) { 1.766 + return aCommand == "cmd_clearhistory" || 1.767 + aCommand == "cmd_togglesuggest"; 1.768 + }, 1.769 + 1.770 + isCommandEnabled: function(aCommand) { 1.771 + return true; 1.772 + }, 1.773 + 1.774 + doCommand: function (aCommand) { 1.775 + switch (aCommand) { 1.776 + case "cmd_clearhistory": 1.777 + var param = this._self.getAttribute("autocompletesearchparam"); 1.778 + 1.779 + let searchBar = this._self.parentNode; 1.780 + 1.781 + BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null); 1.782 + this._self.value = ""; 1.783 + break; 1.784 + case "cmd_togglesuggest": 1.785 + // The pref observer will update _suggestEnabled and the menu 1.786 + // checkmark. 1.787 + this._self._prefBranch.setBoolPref("browser.search.suggest.enabled", 1.788 + !this._self._suggestEnabled); 1.789 + break; 1.790 + default: 1.791 + // do nothing with unrecognized command 1.792 + } 1.793 + } 1.794 + })]]></field> 1.795 + </implementation> 1.796 + 1.797 + <handlers> 1.798 + <handler event="keypress" keycode="VK_UP" modifiers="accel" 1.799 + phase="capturing" 1.800 + action="document.getBindingParent(this).selectEngine(event, false);"/> 1.801 + 1.802 + <handler event="keypress" keycode="VK_DOWN" modifiers="accel" 1.803 + phase="capturing" 1.804 + action="document.getBindingParent(this).selectEngine(event, true);"/> 1.805 + 1.806 + <handler event="keypress" keycode="VK_DOWN" modifiers="alt" 1.807 + phase="capturing" 1.808 + action="return this.openSearch();"/> 1.809 + 1.810 + <handler event="keypress" keycode="VK_UP" modifiers="alt" 1.811 + phase="capturing" 1.812 + action="return this.openSearch();"/> 1.813 + 1.814 +#ifndef XP_MACOSX 1.815 + <handler event="keypress" keycode="VK_F4" 1.816 + phase="capturing" 1.817 + action="return this.openSearch();"/> 1.818 +#endif 1.819 + 1.820 + <handler event="dragover"> 1.821 + <![CDATA[ 1.822 + var types = event.dataTransfer.types; 1.823 + if (types.contains("text/plain") || types.contains("text/x-moz-text-internal")) 1.824 + event.preventDefault(); 1.825 + ]]> 1.826 + </handler> 1.827 + 1.828 + <handler event="drop"> 1.829 + <![CDATA[ 1.830 + var dataTransfer = event.dataTransfer; 1.831 + var data = dataTransfer.getData("text/plain"); 1.832 + if (!data) 1.833 + data = dataTransfer.getData("text/x-moz-text-internal"); 1.834 + if (data) { 1.835 + event.preventDefault(); 1.836 + this.value = data; 1.837 + this.onTextEntered(event); 1.838 + } 1.839 + ]]> 1.840 + </handler> 1.841 + 1.842 + </handlers> 1.843 + </binding> 1.844 +</bindings>