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.

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

mercurial