browser/metro/base/content/bindings/urlbar.xml

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 <?xml version="1.0" encoding="Windows-1252" ?>
     2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
     3    - License, v. 2.0. If a copy of the MPL was not distributed with this
     4    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     7 <!DOCTYPE bindings [
     8 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
     9 %browserDTD;
    10 ]>
    12 <bindings
    13     xmlns="http://www.mozilla.org/xbl"
    14     xmlns:xbl="http://www.mozilla.org/xbl"
    15     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    17   <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
    18     <implementation implements="nsIObserver">
    19       <constructor>
    20         <![CDATA[
    21           this._mayFormat = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
    22           this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
    23           this._maySelectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
    25           Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
    26           Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
    27           Services.prefs.addObserver("browser.urlbar.doubleClickSelectsAll", this, false);
    29           this.inputField.controllers.insertControllerAt(0, this._copyCutValueController);
    31           this.minResultsForPopup = 0;
    32           this.popup._input = this;
    33         ]]>
    34       </constructor>
    36       <destructor>
    37         <![CDATA[
    38           Services.prefs.removeObserver("browser.urlbar.formatting.enabled", this);
    39           Services.prefs.removeObserver("browser.urlbar.trimURLs", this);
    40           Services.prefs.removeObserver("browser.urlbar.doubleClickSelectsAll", this);
    41         ]]>
    42       </destructor>
    44       <field name="_mayFormat"/>
    45       <field name="_mayTrimURLs"/>
    46       <field name="_maySelectAll"/>
    47       <field name="_lastKnownGoodURL"/>
    49       <method name="openPopup">
    50         <body>
    51           <![CDATA[
    52             this.popup.openAutocompletePopup(this, null);
    53           ]]>
    54         </body>
    55       </method>
    57       <method name="closePopup">
    58         <body>
    59           <![CDATA[
    60             this.popup.closePopup(this, null);
    61           ]]>
    62         </body>
    63       </method>
    65       <!-- URI Display: Domain Highlighting -->
    67       <method name="_clearFormatting">
    68         <body>
    69           <![CDATA[
    70             let controller = this.editor.selectionController;
    71             let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
    72             selection.removeAllRanges();
    73           ]]>
    74         </body>
    75       </method>
    77       <method name="formatValue">
    78         <body>
    79           <![CDATA[
    80             if (!this._mayFormat || this.isEditing)
    81               return;
    83             let controller = this.editor.selectionController;
    84             let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
    85             selection.removeAllRanges();
    87             let textNode = this.editor.rootElement.firstChild;
    88             let value = textNode.textContent;
    90             let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
    91             if (protocol &&
    92                 ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
    93               return;
    94             let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
    95             if (!matchedURL)
    96               return;
    98             let [, preDomain, domain] = matchedURL;
    99             let baseDomain = domain;
   100             let subDomain = "";
   101             // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
   102             if (domain[0] != "[") {
   103               try {
   104                 baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
   105                 if (!domain.endsWith(baseDomain)) {
   106                   // getBaseDomainFromHost converts its resultant to ACE.
   107                   let IDNService = Cc["@mozilla.org/network/idn-service;1"]
   108                                    .getService(Ci.nsIIDNService);
   109                   baseDomain = IDNService.convertACEtoUTF8(baseDomain);
   110                 }
   111               } catch (e) {}
   112             }
   113             if (baseDomain != domain) {
   114               subDomain = domain.slice(0, -baseDomain.length);
   115             }
   117             let rangeLength = preDomain.length + subDomain.length;
   118             if (rangeLength) {
   119               let range = document.createRange();
   120               range.setStart(textNode, 0);
   121               range.setEnd(textNode, rangeLength);
   122               selection.addRange(range);
   123             }
   125             let startRest = preDomain.length + domain.length;
   126             if (startRest < value.length) {
   127               let range = document.createRange();
   128               range.setStart(textNode, startRest);
   129               range.setEnd(textNode, value.length);
   130               selection.addRange(range);
   131             }
   132           ]]>
   133         </body>
   134       </method>
   136       <!-- URI Display: Scheme and Trailing Slash Triming -->
   138       <method name="_trimURL">
   139         <parameter name="aURL"/>
   140         <body>
   141           <![CDATA[
   142             // This function must not modify the given URL such that calling
   143             // nsIURIFixup::createFixupURI with the rfdesult will produce a different URI.
   144             return aURL /* remove single trailing slash for http/https/ftp URLs */
   145                .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
   146                 /* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
   147                .replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
   148           ]]>
   149         </body>
   150       </method>
   152       <method name="_getSelectedValueForClipboard">
   153         <body>
   154           <![CDATA[
   155             // Grab the actual input field's value, not our value, which could include moz-action:
   156             let inputVal = this.inputField.value;
   157             let selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd);
   159             // If the selection doesn't start at the beginning or doesn't span the full domain or
   160             // the URL bar is modified, nothing else to do here.
   161             if (this.selectionStart > 0 || this.valueIsTyped)
   162               return selectedVal;
   164             // The selection doesn't span the full domain if it doesn't contain a slash and is
   165             // followed by some character other than a slash.
   166             if (!selectedVal.contains("/")) {
   167               let remainder = inputVal.replace(selectedVal, "");
   168               if (remainder != "" && remainder[0] != "/")
   169                 return selectedVal;
   170             }
   172             let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
   174             let uri;
   175             try {
   176               uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
   177             } catch (e) {}
   178             if (!uri)
   179               return selectedVal;
   181             // Only copy exposable URIs
   182             try {
   183               uri = uriFixup.createExposableURI(uri);
   184             } catch (ex) {}
   186             // If the entire URL is selected, just use the actual loaded URI.
   187             if (inputVal == selectedVal) {
   188               // ... but only if  isn't a javascript: or data: URI, since those
   189               // are hard to read when encoded
   190               if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
   191                 // Parentheses are known to confuse third-party applications (bug 458565).
   192                 selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
   193               }
   195               return selectedVal;
   196             }
   198             // Just the beginning of the URL is selected, check for a trimmed value
   199             let spec = uri.spec;
   200             let trimmedSpec = this._trimURL(spec);
   201             if (spec != trimmedSpec) {
   202               // Prepend the portion that trimURL removed from the beginning.
   203               // This assumes trimURL will only truncate the URL at
   204               // the beginning or end (or both).
   205               let trimmedSegments = spec.split(trimmedSpec);
   206               selectedVal = trimmedSegments[0] + selectedVal;
   207             }
   209             return selectedVal;
   210           ]]>
   211         </body>
   212       </method>
   214       <field name="_copyCutValueController">
   215         <![CDATA[
   216           ({
   217             urlbar: this,
   218             doCommand: function(aCommand) {
   219               let urlbar = this.urlbar;
   220               let val = urlbar._getSelectedValueForClipboard();
   221               if (!val)
   222                 return;
   224               if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
   225                 let start = urlbar.selectionStart;
   226                 let end = urlbar.selectionEnd;
   227                 urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
   228                                           urlbar.inputField.value.substring(end);
   229                 urlbar.selectionStart = urlbar.selectionEnd = start;
   230               }
   232               Cc["@mozilla.org/widget/clipboardhelper;1"]
   233                 .getService(Ci.nsIClipboardHelper)
   234                 .copyString(val, document);
   235             },
   237             supportsCommand: function(aCommand) {
   238               switch (aCommand) {
   239                 case "cmd_copy":
   240                 case "cmd_cut":
   241                   return true;
   242               }
   243               return false;
   244             },
   246             isCommandEnabled: function(aCommand) {
   247               let urlbar = this.urlbar;
   248               return this.supportsCommand(aCommand) &&
   249                      (aCommand != "cmd_cut" || !urlbar.readOnly) &&
   250                      urlbar.selectionStart < urlbar.selectionEnd;
   251             },
   253             onEvent: function(aEventName) {}
   254           })
   255         ]]>
   256       </field>
   258       <method name="trimValue">
   259         <parameter name="aURL"/>
   260         <body>
   261           <![CDATA[
   262             return (this._mayTrimURLs) ? this._trimURL(aURL) : aURL;
   263           ]]>
   264         </body>
   265       </method>
   267       <!-- URI Editing -->
   269       <property name="isEditing" readonly="true">
   270         <getter>
   271           <![CDATA[
   272             return Elements.urlbarState.hasAttribute("editing");
   273           ]]>
   274         </getter>
   275       </property>
   277       <method name="beginEditing">
   278         <parameter name="aShouldDismiss"/>
   279         <body>
   280           <![CDATA[
   281             if (this.isEditing)
   282               return;
   284             Elements.urlbarState.setAttribute("editing", true);
   285             this._lastKnownGoodURL = this.value;
   287             if (!this.focused) {
   288               this.focus();
   289               // If we force focus, ensure we're visible.
   290               if (ContextUI.displayNavbar()) {
   291                 // If we forced visibility, ensure we're positioned above keyboard.
   292                 // (Previous "blur" may have forced us down behind it.)
   293                 ContentAreaObserver.updateAppBarPosition();
   294               }
   295             }
   297             this._clearFormatting();
   298             this.select();
   300             if (aShouldDismiss)
   301               ContextUI.dismissTabs();
   303             if (!InputSourceHelper.isPrecise) {
   304               let inputRectangle = this.inputField.getBoundingClientRect();
   305               SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
   306                   inputRectangle.left, inputRectangle.top, this);
   307             }
   308           ]]>
   309         </body>
   310       </method>
   312       <method name="endEditing">
   313         <parameter name="aShouldRevert"/>
   314         <body>
   315           <![CDATA[
   316             if (!this.isEditing)
   317               return;
   319             Elements.urlbarState.removeAttribute("editing");
   320             this.closePopup();
   321             this.formatValue();
   323             if (this.focused)
   324               this.blur();
   326             if (aShouldRevert)
   327               this.value = this._lastKnownGoodURL;
   328           ]]>
   329         </body>
   330       </method>
   332       <!-- URI Submission -->
   334       <method name="_canonizeURL">
   335         <parameter name="aURL"/>
   336         <parameter name="aTriggeringEvent"/>
   337         <body>
   338           <![CDATA[
   339             if (!aURL)
   340               return "";
   342             // Only add the suffix when the URL bar value isn't already "URL-like",
   343             // and only if we get a keyboard event, to match user expectations.
   344             if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aURL)) {
   345               let accel = aTriggeringEvent.ctrlKey;
   346               let shift = aTriggeringEvent.shiftKey;
   347               let suffix = "";
   349               switch (true) {
   350                 case (accel && shift):
   351                   suffix = ".org/";
   352                   break;
   353                 case (shift):
   354                   suffix = ".net/";
   355                   break;
   356                 case (accel):
   357                   try {
   358                     suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
   359                     if (suffix.charAt(suffix.length - 1) != "/")
   360                       suffix += "/";
   361                   } catch(e) {
   362                     suffix = ".com/";
   363                   }
   364                   break;
   365               }
   367               if (suffix) {
   368                 // trim leading/trailing spaces (bug 233205)
   369                 aURL = aURL.trim();
   371                 // Tack www. and suffix on.  If user has appended directories, insert
   372                 // suffix before them (bug 279035).  Be careful not to get two slashes.
   373                 let firstSlash = aURL.indexOf("/");
   374                 if (firstSlash >= 0) {
   375                   aURL = aURL.substring(0, firstSlash) + suffix + aURL.substring(firstSlash + 1);
   376                 } else {
   377                   aURL = aURL + suffix;
   378                 }
   379                 aURL = "http://www." + aURL;
   380               }
   381             }
   383             return aURL;
   384           ]]>
   385         </body>
   386       </method>
   388       <method name="submitURL">
   389         <parameter name="aEvent"/>
   390         <body>
   391           <![CDATA[
   392             // If the address was typed in by a user, tidy it up
   393             if (aEvent instanceof KeyEvent)
   394               this.value = this._canonizeURL(this.value, aEvent);
   396             this.endEditing();
   397             BrowserUI.goToURI(this.value);
   399             return true;
   400           ]]>
   401         </body>
   402       </method>
   404       <method name="submitSearch">
   405         <parameter name="anEngineName"/>
   406         <body>
   407           <![CDATA[
   408             this.endEditing();
   409             BrowserUI.doOpenSearch(anEngineName);
   411             return true;
   412           ]]>
   413         </body>
   414       </method>
   416       <!-- nsIObserver -->
   418       <method name="observe">
   419         <parameter name="aSubject"/>
   420         <parameter name="aTopic"/>
   421         <parameter name="aData"/>
   422         <body>
   423           <![CDATA[
   424             if (aTopic != "nsPref:changed")
   425               return;
   427             switch (aData) {
   428               case "browser.urlbar.formatting.enabled":
   429                 this._mayFormat = Services.prefs.getBoolPref(aData);
   430                 if (!this._mayFormat) this._clearFormatting();
   431                 break;
   432               case "browser.urlbar.trimURLs":
   433                 this._mayTrimURLs = Services.prefs.getBoolPref(aData);
   434                 break;
   435               case "browser.urlbar.doubleClickSelectsAll":
   436                 this._maySelectAll = Services.prefs.getBoolPref(aData);
   437                 break;
   438             }
   439           ]]>
   440         </body>
   441       </method>
   442     </implementation>
   444     <handlers>
   445       <!-- Entering editing mode -->
   447       <handler event="focus" phase="capturing">
   448         <![CDATA[
   449           this.beginEditing();
   450         ]]>
   451       </handler>
   453       <handler event="input" phase="capturing">
   454         <![CDATA[
   455           // Ensures that paste-and-go actually brings the URL bar into editing mode
   456           // and displays the half-height autocomplete popup.
   457           this.beginEditing();
   458           this.openPopup();
   459         ]]>
   460       </handler>
   462       <handler event="click" phase="capturing">
   463         <![CDATA[
   464           // workaround for bug 925457: taping browser chrome resets last tap
   465           // co-ordinates to 'undefined' so that we know not to shift the
   466           // browser when the keyboard is up in SelectionHandler's
   467           // _calcNewContentPosition().
   468           Browser.selectedTab.browser.messageManager.sendAsyncMessage(
   469               "Browser:ResetLastPos", {
   470                 xPos: null,
   471                 yPos: null
   472               });
   473           this.beginEditing(true);
   474         ]]>
   475       </handler>
   477       <!-- Editing mode behaviors -->
   479       <handler event="dblclick" phase="capturing">
   480         <![CDATA[
   481             if (this._maySelectAll) this.select();
   482         ]]>
   483       </handler>
   485       <handler event="contextmenu" phase="capturing">
   486         <![CDATA[
   487           let box = this.inputField.parentNode;
   488           box.showContextMenu(this, event, true);
   489         ]]>
   490       </handler>
   492       <!-- Leaving editing mode -->
   494       <handler event="blur" phase="capturing">
   495         <![CDATA[
   496           this.endEditing();
   497         ]]>
   498       </handler>
   500       <handler event="keypress" phase="capturing" keycode="VK_RETURN"
   501                modifiers="accel shift any">
   502         <![CDATA[
   503           if (this.popup.submitSelected())
   504             return;
   506           if (this.submitURL(event))
   507             return;
   508         ]]>
   509       </handler>
   511       <handler event="keypress" phase="capturing" keycode="VK_UP">
   512         <![CDATA[
   513           if (!this.popup.hasSelection) {
   514             // Treat the first up as a down key to trick handleKeyNavigation() to start
   515             // keyboard navigation on autocomplete popup.
   516             this.mController.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
   517             event.preventDefault();
   518           }
   519         ]]>
   520       </handler>
   521     </handlers>
   522   </binding>
   524   <binding id="urlbar-autocomplete">
   525     <content class="meta-section-container" pack="end">
   526       <xul:vbox class="meta-section" anonid="results-container" flex="2">
   527         <xul:label class="meta-section-title"
   528                    value="&autocompleteResultsHeader.label;"/>
   529         <richgrid anonid="results" rows="3" minSlots="8"
   530                   seltype="single" nocontext="true" deferlayout="true"/>
   531       </xul:vbox>
   533       <xul:vbox class="meta-section" flex="1">
   534         <xul:label anonid="searches-header" class="meta-section-title"/>
   535         <richgrid anonid="searches" rows="3" flex="1" search="true"
   536                   seltype="single" nocontext="true" deferlayout="true"/>
   537       </xul:vbox>
   538     </content>
   540     <implementation implements="nsIAutoCompletePopup, nsIObserver">
   541       <constructor>
   542         <![CDATA[
   543           this.hidden = true;
   544           Services.obs.addObserver(this, "browser-search-engine-modified", false);
   546           this._results.controller = this;
   547           this._searches.controller = this;
   548         ]]>
   549       </constructor>
   551       <destructor>
   552         <![CDATA[
   553           Services.obs.removeObserver(this, "browser-search-engine-modified");
   554         ]]>
   555       </destructor>
   557       <!-- nsIAutocompleteInput -->
   559       <field name="_input">null</field>
   561       <property name="input" readonly="true" onget="return this._input;"/>
   562       <property name="matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
   563       <property name="popupOpen" readonly="true" onget="return !this.hidden"/>
   564       <property name="overrideValue" readonly="true" onget="return null;"/>
   566       <property name="selectedItem">
   567         <getter>
   568           <![CDATA[
   569             return this._isGridBound(this._results) ? this._results.selectedItem : null;
   570           ]]>
   571         </getter>
   572         <setter>
   573           <![CDATA[
   574             if (this._isGridBound(this._results)) {
   575               this._results.selectedItem = val;
   576             }
   577           ]]>
   578         </setter>
   579       </property>
   581       <property name="selectedIndex">
   582         <getter>
   583           <![CDATA[
   584             return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
   585           ]]>
   586         </getter>
   587         <setter>
   588           <![CDATA[
   589             if (this._isGridBound(this._results)) {
   590               this._results.selectedIndex = val;
   591             }
   592           ]]>
   593         </setter>
   594       </property>
   596       <property name="hasSelection">
   597         <getter>
   598           <![CDATA[
   599             if (!this._isGridBound(this._results) ||
   600               !this._isGridBound(this._searches))
   601               return false;
   603             return (this._results.selectedIndex >= 0 ||
   604               this._searches.selectedIndex >= 0);
   605           ]]>
   606         </getter>
   607       </property>
   609       <method name="openAutocompletePopup">
   610         <parameter name="aInput"/>
   611         <parameter name="aElement"/>
   612         <body>
   613           <![CDATA[
   614             if (this.popupOpen)
   615               return;
   617             ContextUI.dismissContextAppbar();
   619             this._input = aInput;
   620             this._grid = this._results;
   622             this.clearSelection();
   623             this.invalidate();
   625             let viewstate = this.getAttribute("viewstate");
   626             switch (viewstate) {
   627               case "portrait":
   628               case "snapped":
   629                 this._results.setAttribute("vertical", true);
   630                 break;
   631               default:
   632                 this._results.removeAttribute("vertical");
   633                 break;
   634             }
   636             this._results.arrangeItemsNow();
   637             this._searches.arrangeItemsNow();
   639             this.hidden = false;
   640             Elements.urlbarState.setAttribute("autocomplete", "true");
   641           ]]>
   642         </body>
   643       </method>
   645       <method name="closePopup">
   646         <body>
   647           <![CDATA[
   648             if (!this.popupOpen)
   649               return;
   651             this.input.controller.stopSearch();
   652             this.hidden = true;
   653             Elements.urlbarState.removeAttribute("autocomplete");
   654           ]]>
   655         </body>
   656       </method>
   658       <!-- Updating grid content -->
   660       <field name="_grid">null</field>
   662       <field name="_results" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results');</field>
   663       <field name="_resultsContainer" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results-container');</field>
   665       <field name="_searchesHeader" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches-header');</field>
   666       <field name="_searches" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches');</field>
   668       <property name="_otherGrid" readonly="true">
   669         <getter>
   670           <![CDATA[
   671             if (this._grid == null)
   672               return null;
   674             return (this._grid == this._results) ? this._searches : this._results;
   675           ]]>
   676         </getter>
   677       </property>
   679       <method name="_isGridBound">
   680         <parameter name="aGrid"/>
   681         <body>
   682           <![CDATA[
   683             return aGrid && aGrid.isBound;
   684           ]]>
   685         </body>
   686       </method>
   688       <method name="invalidate">
   689         <body>
   690           <![CDATA[
   691             if (!this.popupOpen)
   692               return;
   694             this.updateSearchEngineHeader();
   695             this.updateResults();
   696           ]]>
   697         </body>
   698       </method>
   700       <!-- Updating grid content: results -->
   702       <method name="updateResults">
   703         <body>
   704           <![CDATA[
   705             if (!this._isGridBound(this._results))
   706               return;
   708             if (!this.input)
   709               return;
   711             let haveNoResults = (this.matchCount == 0);
   713             if (haveNoResults) {
   714               this._results.clearAll();
   715               this.setAttribute("nomatch", true);
   716               this._resultsContainer.removeAttribute("flex");
   717               return;
   718             }
   720             this.removeAttribute("nomatch");
   721             this._resultsContainer.setAttribute("flex", 1);
   723             let controller = this.input.controller;
   724             let lastMatch = this.matchCount - 1;
   725             let iterCount = Math.max(this._results.itemCount, this.matchCount);
   727             // Swap out existing items for new search hit results
   728             for (let i = 0; i < iterCount; i++) {
   729               let item = this._results._slotAt(i);
   730               if (i > lastMatch) {
   731                 item.removeAttribute("value");
   732                 item.removeAttribute("autocomplete");
   733                 continue;
   734               }
   736               let value = controller.getValueAt(i);
   737               let label = controller.getCommentAt(i) || value;
   738               let iconURI = controller.getImageAt(i);
   740               item.setAttribute("autocomplete", true);
   741               item.setAttribute("label", label);
   742               item.setAttribute("value", value);
   743               item.setAttribute("iconURI", iconURI);
   744               let xpFaviconURI = Services.io.newURI(iconURI.replace("moz-anno:favicon:",""), null, null);
   745               Task.spawn(function() {
   746                 let colorInfo = yield ColorUtils.getForegroundAndBackgroundIconColors(xpFaviconURI);
   747                 if ( !(colorInfo && colorInfo.background && colorInfo.foreground) 
   748                      || (item.getAttribute("iconURI") != iconURI) ) {
   749                   return;
   750                 }
   751                 let { background, foreground } = colorInfo;
   752                 item.style.color = foreground; //color text
   753                 item.setAttribute("customColor", background);
   754                 // when bound, use the setter to propogate the color change through the tile
   755                 if ('color' in item) {
   756                   item.color = background;
   757                 }
   758               }).then(null, err => Components.utils.reportError(err));
   759             }
   761             this._results.arrangeItems();
   762           ]]>
   763         </body>
   764       </method>
   766       <!-- Updating grid content: search engines -->
   768       <field name="_engines">[]</field>
   770       <method name="_initSearchEngines">
   771         <body>
   772           <![CDATA[
   773             Services.search.init(this.updateSearchEngineGrid.bind(this));
   774           ]]>
   775         </body>
   776       </method>
   778       <method name="updateSearchEngineGrid">
   779         <body>
   780           <![CDATA[
   781             if (!this._isGridBound(this._searches))
   782               return;
   784             this._engines = Services.search.getVisibleEngines();
   786             while (this._searches.itemCount > 0)
   787               this._searches.removeItemAt(0, true);
   789             this._engines.forEach(function (anEngine) {
   790               let item = this._searches.appendItem("", anEngine.name, true);
   791               item.setAttribute("autocomplete", "true");
   792               item.setAttribute("search", "true");
   794               let largeImage = anEngine.getIconURLBySize(74,74);
   795               if (largeImage) {
   796                 item.setAttribute("iconsize", "large");
   797                 item.setAttribute("iconURI", largeImage);
   798               } else if (anEngine.iconURI && anEngine.iconURI.spec) {
   799                 item.setAttribute("iconURI", anEngine.iconURI.spec);
   800                 item.setAttribute("customColor", "#fff");
   801               }
   802             }.bind(this));
   804             this._searches.arrangeItems();
   805           ]]>
   806         </body>
   807       </method>
   809       <method name="updateSearchEngineHeader">
   810         <body>
   811           <![CDATA[
   812             if (!this._isGridBound(this._searches))
   813               return;
   815             let searchString = this.input.controller.searchString;
   816             let label = Strings.browser.formatStringFromName(
   817                               "opensearch.search.header", [searchString], 1);
   819             this._searchesHeader.value = label;
   820           ]]>
   821         </body>
   822       </method>
   824       <!-- Selecting results -->
   826       <method name="selectBy">
   827         <parameter name="aReverse"/>
   828         <parameter name="aPage"/>
   829         <body>
   830           <![CDATA[
   831             if (!this._isGridBound(this._results) ||
   832                 !this._isGridBound(this._searches))
   833               return;
   835             // Move between grids if we're at the edge of one.
   836             if ((this._grid.isSelectionAtEnd && !aReverse) ||
   837                 (this._grid.isSelectionAtStart && aReverse)) {
   838               let index = !aReverse ? 0 : this._otherGrid.itemCount - 1;
   839               this._otherGrid.selectedIndex = index;
   840             } else {
   841               this._grid.offsetSelection(aReverse ? -1 : 1);
   842             }
   843           ]]>
   844         </body>
   845       </method>
   847       <method name="clearSelection">
   848         <body>
   849           <![CDATA[
   850             if (this._isGridBound(this._results))
   851               this._results.clearSelection();
   853             if (this._isGridBound(this._searches))
   854               this._searches.clearSelection();
   855           ]]>
   856         </body>
   857       </method>
   859       <!-- Submitting selected results -->
   861       <method name="submitSelected">
   862         <body>
   863           <![CDATA[
   864             if (this._isGridBound(this._results) &&
   865                 this._results.selectedIndex >= 0) {
   866               let url = this.input.controller.getValueAt(this._results.selectedIndex);
   867               this.input.value = url;
   868               return this.input.submitURL();
   869             }
   871             if (this._isGridBound(this._searches) &&
   872                 this._searches.selectedIndex >= 0) {
   873               let engine = this._engines[this._searches.selectedIndex];
   874               return this.input.submitSearch(engine.name);
   875             }
   877             return false;
   878           ]]>
   879         </body>
   880       </method>
   882       <method name="handleItemClick">
   883         <parameter name="aItem"/>
   884         <parameter name="aEvent"/>
   885         <body>
   886           <![CDATA[
   887             this.submitSelected();
   888           ]]>
   889         </body>
   890       </method>
   892       <!-- nsIObserver -->
   894       <method name="observe">
   895         <parameter name="aSubject"/>
   896         <parameter name="aTopic"/>
   897         <parameter name="aData"/>
   898         <body>
   899           <![CDATA[
   900             switch (aTopic) {
   901               case "browser-search-engine-modified":
   902                 this.updateSearchEngineGrid();
   903                 break;
   904             }
   905           ]]>
   906         </body>
   907       </method>
   908     </implementation>
   910     <handlers>
   911       <handler event="contentgenerated">
   912         <![CDATA[
   913           let grid = event.originalTarget;
   915           if (grid == this._searches)
   916             this._initSearchEngines();
   918           if (grid == this._results)
   919             this.updateResults();
   920         ]]>
   921       </handler>
   923       <handler event="select">
   924         <![CDATA[
   925           let grid = event.originalTarget;
   927           // If a selection was made on a different grid,
   928           // remove selection from the current grid.
   929           if (grid != this._grid) {
   930             this._grid.clearSelection();
   931             this._grid = this._otherGrid;
   932           }
   933         ]]>
   934       </handler>
   935     </handlers>
   936   </binding>
   937 </bindings>

mercurial