1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/content/bindings/urlbar.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,937 @@ 1.4 +<?xml version="1.0" encoding="Windows-1252" ?> 1.5 +<!-- This Source Code Form is subject to the terms of the Mozilla Public 1.6 + - License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 1.8 + 1.9 + 1.10 +<!DOCTYPE bindings [ 1.11 +<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> 1.12 +%browserDTD; 1.13 +]> 1.14 + 1.15 +<bindings 1.16 + xmlns="http://www.mozilla.org/xbl" 1.17 + xmlns:xbl="http://www.mozilla.org/xbl" 1.18 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 1.19 + 1.20 + <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> 1.21 + <implementation implements="nsIObserver"> 1.22 + <constructor> 1.23 + <![CDATA[ 1.24 + this._mayFormat = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled"); 1.25 + this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs"); 1.26 + this._maySelectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll"); 1.27 + 1.28 + Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false); 1.29 + Services.prefs.addObserver("browser.urlbar.trimURLs", this, false); 1.30 + Services.prefs.addObserver("browser.urlbar.doubleClickSelectsAll", this, false); 1.31 + 1.32 + this.inputField.controllers.insertControllerAt(0, this._copyCutValueController); 1.33 + 1.34 + this.minResultsForPopup = 0; 1.35 + this.popup._input = this; 1.36 + ]]> 1.37 + </constructor> 1.38 + 1.39 + <destructor> 1.40 + <![CDATA[ 1.41 + Services.prefs.removeObserver("browser.urlbar.formatting.enabled", this); 1.42 + Services.prefs.removeObserver("browser.urlbar.trimURLs", this); 1.43 + Services.prefs.removeObserver("browser.urlbar.doubleClickSelectsAll", this); 1.44 + ]]> 1.45 + </destructor> 1.46 + 1.47 + <field name="_mayFormat"/> 1.48 + <field name="_mayTrimURLs"/> 1.49 + <field name="_maySelectAll"/> 1.50 + <field name="_lastKnownGoodURL"/> 1.51 + 1.52 + <method name="openPopup"> 1.53 + <body> 1.54 + <![CDATA[ 1.55 + this.popup.openAutocompletePopup(this, null); 1.56 + ]]> 1.57 + </body> 1.58 + </method> 1.59 + 1.60 + <method name="closePopup"> 1.61 + <body> 1.62 + <![CDATA[ 1.63 + this.popup.closePopup(this, null); 1.64 + ]]> 1.65 + </body> 1.66 + </method> 1.67 + 1.68 + <!-- URI Display: Domain Highlighting --> 1.69 + 1.70 + <method name="_clearFormatting"> 1.71 + <body> 1.72 + <![CDATA[ 1.73 + let controller = this.editor.selectionController; 1.74 + let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); 1.75 + selection.removeAllRanges(); 1.76 + ]]> 1.77 + </body> 1.78 + </method> 1.79 + 1.80 + <method name="formatValue"> 1.81 + <body> 1.82 + <![CDATA[ 1.83 + if (!this._mayFormat || this.isEditing) 1.84 + return; 1.85 + 1.86 + let controller = this.editor.selectionController; 1.87 + let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); 1.88 + selection.removeAllRanges(); 1.89 + 1.90 + let textNode = this.editor.rootElement.firstChild; 1.91 + let value = textNode.textContent; 1.92 + 1.93 + let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/); 1.94 + if (protocol && 1.95 + ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1) 1.96 + return; 1.97 + let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/); 1.98 + if (!matchedURL) 1.99 + return; 1.100 + 1.101 + let [, preDomain, domain] = matchedURL; 1.102 + let baseDomain = domain; 1.103 + let subDomain = ""; 1.104 + // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159) 1.105 + if (domain[0] != "[") { 1.106 + try { 1.107 + baseDomain = Services.eTLD.getBaseDomainFromHost(domain); 1.108 + if (!domain.endsWith(baseDomain)) { 1.109 + // getBaseDomainFromHost converts its resultant to ACE. 1.110 + let IDNService = Cc["@mozilla.org/network/idn-service;1"] 1.111 + .getService(Ci.nsIIDNService); 1.112 + baseDomain = IDNService.convertACEtoUTF8(baseDomain); 1.113 + } 1.114 + } catch (e) {} 1.115 + } 1.116 + if (baseDomain != domain) { 1.117 + subDomain = domain.slice(0, -baseDomain.length); 1.118 + } 1.119 + 1.120 + let rangeLength = preDomain.length + subDomain.length; 1.121 + if (rangeLength) { 1.122 + let range = document.createRange(); 1.123 + range.setStart(textNode, 0); 1.124 + range.setEnd(textNode, rangeLength); 1.125 + selection.addRange(range); 1.126 + } 1.127 + 1.128 + let startRest = preDomain.length + domain.length; 1.129 + if (startRest < value.length) { 1.130 + let range = document.createRange(); 1.131 + range.setStart(textNode, startRest); 1.132 + range.setEnd(textNode, value.length); 1.133 + selection.addRange(range); 1.134 + } 1.135 + ]]> 1.136 + </body> 1.137 + </method> 1.138 + 1.139 + <!-- URI Display: Scheme and Trailing Slash Triming --> 1.140 + 1.141 + <method name="_trimURL"> 1.142 + <parameter name="aURL"/> 1.143 + <body> 1.144 + <![CDATA[ 1.145 + // This function must not modify the given URL such that calling 1.146 + // nsIURIFixup::createFixupURI with the rfdesult will produce a different URI. 1.147 + return aURL /* remove single trailing slash for http/https/ftp URLs */ 1.148 + .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1") 1.149 + /* remove http:// unless the host starts with "ftp\d*\." or contains "@" */ 1.150 + .replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1"); 1.151 + ]]> 1.152 + </body> 1.153 + </method> 1.154 + 1.155 + <method name="_getSelectedValueForClipboard"> 1.156 + <body> 1.157 + <![CDATA[ 1.158 + // Grab the actual input field's value, not our value, which could include moz-action: 1.159 + let inputVal = this.inputField.value; 1.160 + let selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd); 1.161 + 1.162 + // If the selection doesn't start at the beginning or doesn't span the full domain or 1.163 + // the URL bar is modified, nothing else to do here. 1.164 + if (this.selectionStart > 0 || this.valueIsTyped) 1.165 + return selectedVal; 1.166 + 1.167 + // The selection doesn't span the full domain if it doesn't contain a slash and is 1.168 + // followed by some character other than a slash. 1.169 + if (!selectedVal.contains("/")) { 1.170 + let remainder = inputVal.replace(selectedVal, ""); 1.171 + if (remainder != "" && remainder[0] != "/") 1.172 + return selectedVal; 1.173 + } 1.174 + 1.175 + let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup); 1.176 + 1.177 + let uri; 1.178 + try { 1.179 + uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE); 1.180 + } catch (e) {} 1.181 + if (!uri) 1.182 + return selectedVal; 1.183 + 1.184 + // Only copy exposable URIs 1.185 + try { 1.186 + uri = uriFixup.createExposableURI(uri); 1.187 + } catch (ex) {} 1.188 + 1.189 + // If the entire URL is selected, just use the actual loaded URI. 1.190 + if (inputVal == selectedVal) { 1.191 + // ... but only if isn't a javascript: or data: URI, since those 1.192 + // are hard to read when encoded 1.193 + if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) { 1.194 + // Parentheses are known to confuse third-party applications (bug 458565). 1.195 + selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c)); 1.196 + } 1.197 + 1.198 + return selectedVal; 1.199 + } 1.200 + 1.201 + // Just the beginning of the URL is selected, check for a trimmed value 1.202 + let spec = uri.spec; 1.203 + let trimmedSpec = this._trimURL(spec); 1.204 + if (spec != trimmedSpec) { 1.205 + // Prepend the portion that trimURL removed from the beginning. 1.206 + // This assumes trimURL will only truncate the URL at 1.207 + // the beginning or end (or both). 1.208 + let trimmedSegments = spec.split(trimmedSpec); 1.209 + selectedVal = trimmedSegments[0] + selectedVal; 1.210 + } 1.211 + 1.212 + return selectedVal; 1.213 + ]]> 1.214 + </body> 1.215 + </method> 1.216 + 1.217 + <field name="_copyCutValueController"> 1.218 + <![CDATA[ 1.219 + ({ 1.220 + urlbar: this, 1.221 + doCommand: function(aCommand) { 1.222 + let urlbar = this.urlbar; 1.223 + let val = urlbar._getSelectedValueForClipboard(); 1.224 + if (!val) 1.225 + return; 1.226 + 1.227 + if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) { 1.228 + let start = urlbar.selectionStart; 1.229 + let end = urlbar.selectionEnd; 1.230 + urlbar.inputField.value = urlbar.inputField.value.substring(0, start) + 1.231 + urlbar.inputField.value.substring(end); 1.232 + urlbar.selectionStart = urlbar.selectionEnd = start; 1.233 + } 1.234 + 1.235 + Cc["@mozilla.org/widget/clipboardhelper;1"] 1.236 + .getService(Ci.nsIClipboardHelper) 1.237 + .copyString(val, document); 1.238 + }, 1.239 + 1.240 + supportsCommand: function(aCommand) { 1.241 + switch (aCommand) { 1.242 + case "cmd_copy": 1.243 + case "cmd_cut": 1.244 + return true; 1.245 + } 1.246 + return false; 1.247 + }, 1.248 + 1.249 + isCommandEnabled: function(aCommand) { 1.250 + let urlbar = this.urlbar; 1.251 + return this.supportsCommand(aCommand) && 1.252 + (aCommand != "cmd_cut" || !urlbar.readOnly) && 1.253 + urlbar.selectionStart < urlbar.selectionEnd; 1.254 + }, 1.255 + 1.256 + onEvent: function(aEventName) {} 1.257 + }) 1.258 + ]]> 1.259 + </field> 1.260 + 1.261 + <method name="trimValue"> 1.262 + <parameter name="aURL"/> 1.263 + <body> 1.264 + <![CDATA[ 1.265 + return (this._mayTrimURLs) ? this._trimURL(aURL) : aURL; 1.266 + ]]> 1.267 + </body> 1.268 + </method> 1.269 + 1.270 + <!-- URI Editing --> 1.271 + 1.272 + <property name="isEditing" readonly="true"> 1.273 + <getter> 1.274 + <![CDATA[ 1.275 + return Elements.urlbarState.hasAttribute("editing"); 1.276 + ]]> 1.277 + </getter> 1.278 + </property> 1.279 + 1.280 + <method name="beginEditing"> 1.281 + <parameter name="aShouldDismiss"/> 1.282 + <body> 1.283 + <![CDATA[ 1.284 + if (this.isEditing) 1.285 + return; 1.286 + 1.287 + Elements.urlbarState.setAttribute("editing", true); 1.288 + this._lastKnownGoodURL = this.value; 1.289 + 1.290 + if (!this.focused) { 1.291 + this.focus(); 1.292 + // If we force focus, ensure we're visible. 1.293 + if (ContextUI.displayNavbar()) { 1.294 + // If we forced visibility, ensure we're positioned above keyboard. 1.295 + // (Previous "blur" may have forced us down behind it.) 1.296 + ContentAreaObserver.updateAppBarPosition(); 1.297 + } 1.298 + } 1.299 + 1.300 + this._clearFormatting(); 1.301 + this.select(); 1.302 + 1.303 + if (aShouldDismiss) 1.304 + ContextUI.dismissTabs(); 1.305 + 1.306 + if (!InputSourceHelper.isPrecise) { 1.307 + let inputRectangle = this.inputField.getBoundingClientRect(); 1.308 + SelectionHelperUI.attachEditSession(ChromeSelectionHandler, 1.309 + inputRectangle.left, inputRectangle.top, this); 1.310 + } 1.311 + ]]> 1.312 + </body> 1.313 + </method> 1.314 + 1.315 + <method name="endEditing"> 1.316 + <parameter name="aShouldRevert"/> 1.317 + <body> 1.318 + <![CDATA[ 1.319 + if (!this.isEditing) 1.320 + return; 1.321 + 1.322 + Elements.urlbarState.removeAttribute("editing"); 1.323 + this.closePopup(); 1.324 + this.formatValue(); 1.325 + 1.326 + if (this.focused) 1.327 + this.blur(); 1.328 + 1.329 + if (aShouldRevert) 1.330 + this.value = this._lastKnownGoodURL; 1.331 + ]]> 1.332 + </body> 1.333 + </method> 1.334 + 1.335 + <!-- URI Submission --> 1.336 + 1.337 + <method name="_canonizeURL"> 1.338 + <parameter name="aURL"/> 1.339 + <parameter name="aTriggeringEvent"/> 1.340 + <body> 1.341 + <![CDATA[ 1.342 + if (!aURL) 1.343 + return ""; 1.344 + 1.345 + // Only add the suffix when the URL bar value isn't already "URL-like", 1.346 + // and only if we get a keyboard event, to match user expectations. 1.347 + if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aURL)) { 1.348 + let accel = aTriggeringEvent.ctrlKey; 1.349 + let shift = aTriggeringEvent.shiftKey; 1.350 + let suffix = ""; 1.351 + 1.352 + switch (true) { 1.353 + case (accel && shift): 1.354 + suffix = ".org/"; 1.355 + break; 1.356 + case (shift): 1.357 + suffix = ".net/"; 1.358 + break; 1.359 + case (accel): 1.360 + try { 1.361 + suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix"); 1.362 + if (suffix.charAt(suffix.length - 1) != "/") 1.363 + suffix += "/"; 1.364 + } catch(e) { 1.365 + suffix = ".com/"; 1.366 + } 1.367 + break; 1.368 + } 1.369 + 1.370 + if (suffix) { 1.371 + // trim leading/trailing spaces (bug 233205) 1.372 + aURL = aURL.trim(); 1.373 + 1.374 + // Tack www. and suffix on. If user has appended directories, insert 1.375 + // suffix before them (bug 279035). Be careful not to get two slashes. 1.376 + let firstSlash = aURL.indexOf("/"); 1.377 + if (firstSlash >= 0) { 1.378 + aURL = aURL.substring(0, firstSlash) + suffix + aURL.substring(firstSlash + 1); 1.379 + } else { 1.380 + aURL = aURL + suffix; 1.381 + } 1.382 + aURL = "http://www." + aURL; 1.383 + } 1.384 + } 1.385 + 1.386 + return aURL; 1.387 + ]]> 1.388 + </body> 1.389 + </method> 1.390 + 1.391 + <method name="submitURL"> 1.392 + <parameter name="aEvent"/> 1.393 + <body> 1.394 + <![CDATA[ 1.395 + // If the address was typed in by a user, tidy it up 1.396 + if (aEvent instanceof KeyEvent) 1.397 + this.value = this._canonizeURL(this.value, aEvent); 1.398 + 1.399 + this.endEditing(); 1.400 + BrowserUI.goToURI(this.value); 1.401 + 1.402 + return true; 1.403 + ]]> 1.404 + </body> 1.405 + </method> 1.406 + 1.407 + <method name="submitSearch"> 1.408 + <parameter name="anEngineName"/> 1.409 + <body> 1.410 + <![CDATA[ 1.411 + this.endEditing(); 1.412 + BrowserUI.doOpenSearch(anEngineName); 1.413 + 1.414 + return true; 1.415 + ]]> 1.416 + </body> 1.417 + </method> 1.418 + 1.419 + <!-- nsIObserver --> 1.420 + 1.421 + <method name="observe"> 1.422 + <parameter name="aSubject"/> 1.423 + <parameter name="aTopic"/> 1.424 + <parameter name="aData"/> 1.425 + <body> 1.426 + <![CDATA[ 1.427 + if (aTopic != "nsPref:changed") 1.428 + return; 1.429 + 1.430 + switch (aData) { 1.431 + case "browser.urlbar.formatting.enabled": 1.432 + this._mayFormat = Services.prefs.getBoolPref(aData); 1.433 + if (!this._mayFormat) this._clearFormatting(); 1.434 + break; 1.435 + case "browser.urlbar.trimURLs": 1.436 + this._mayTrimURLs = Services.prefs.getBoolPref(aData); 1.437 + break; 1.438 + case "browser.urlbar.doubleClickSelectsAll": 1.439 + this._maySelectAll = Services.prefs.getBoolPref(aData); 1.440 + break; 1.441 + } 1.442 + ]]> 1.443 + </body> 1.444 + </method> 1.445 + </implementation> 1.446 + 1.447 + <handlers> 1.448 + <!-- Entering editing mode --> 1.449 + 1.450 + <handler event="focus" phase="capturing"> 1.451 + <![CDATA[ 1.452 + this.beginEditing(); 1.453 + ]]> 1.454 + </handler> 1.455 + 1.456 + <handler event="input" phase="capturing"> 1.457 + <![CDATA[ 1.458 + // Ensures that paste-and-go actually brings the URL bar into editing mode 1.459 + // and displays the half-height autocomplete popup. 1.460 + this.beginEditing(); 1.461 + this.openPopup(); 1.462 + ]]> 1.463 + </handler> 1.464 + 1.465 + <handler event="click" phase="capturing"> 1.466 + <![CDATA[ 1.467 + // workaround for bug 925457: taping browser chrome resets last tap 1.468 + // co-ordinates to 'undefined' so that we know not to shift the 1.469 + // browser when the keyboard is up in SelectionHandler's 1.470 + // _calcNewContentPosition(). 1.471 + Browser.selectedTab.browser.messageManager.sendAsyncMessage( 1.472 + "Browser:ResetLastPos", { 1.473 + xPos: null, 1.474 + yPos: null 1.475 + }); 1.476 + this.beginEditing(true); 1.477 + ]]> 1.478 + </handler> 1.479 + 1.480 + <!-- Editing mode behaviors --> 1.481 + 1.482 + <handler event="dblclick" phase="capturing"> 1.483 + <![CDATA[ 1.484 + if (this._maySelectAll) this.select(); 1.485 + ]]> 1.486 + </handler> 1.487 + 1.488 + <handler event="contextmenu" phase="capturing"> 1.489 + <![CDATA[ 1.490 + let box = this.inputField.parentNode; 1.491 + box.showContextMenu(this, event, true); 1.492 + ]]> 1.493 + </handler> 1.494 + 1.495 + <!-- Leaving editing mode --> 1.496 + 1.497 + <handler event="blur" phase="capturing"> 1.498 + <![CDATA[ 1.499 + this.endEditing(); 1.500 + ]]> 1.501 + </handler> 1.502 + 1.503 + <handler event="keypress" phase="capturing" keycode="VK_RETURN" 1.504 + modifiers="accel shift any"> 1.505 + <![CDATA[ 1.506 + if (this.popup.submitSelected()) 1.507 + return; 1.508 + 1.509 + if (this.submitURL(event)) 1.510 + return; 1.511 + ]]> 1.512 + </handler> 1.513 + 1.514 + <handler event="keypress" phase="capturing" keycode="VK_UP"> 1.515 + <![CDATA[ 1.516 + if (!this.popup.hasSelection) { 1.517 + // Treat the first up as a down key to trick handleKeyNavigation() to start 1.518 + // keyboard navigation on autocomplete popup. 1.519 + this.mController.handleKeyNavigation(KeyEvent.DOM_VK_DOWN); 1.520 + event.preventDefault(); 1.521 + } 1.522 + ]]> 1.523 + </handler> 1.524 + </handlers> 1.525 + </binding> 1.526 + 1.527 + <binding id="urlbar-autocomplete"> 1.528 + <content class="meta-section-container" pack="end"> 1.529 + <xul:vbox class="meta-section" anonid="results-container" flex="2"> 1.530 + <xul:label class="meta-section-title" 1.531 + value="&autocompleteResultsHeader.label;"/> 1.532 + <richgrid anonid="results" rows="3" minSlots="8" 1.533 + seltype="single" nocontext="true" deferlayout="true"/> 1.534 + </xul:vbox> 1.535 + 1.536 + <xul:vbox class="meta-section" flex="1"> 1.537 + <xul:label anonid="searches-header" class="meta-section-title"/> 1.538 + <richgrid anonid="searches" rows="3" flex="1" search="true" 1.539 + seltype="single" nocontext="true" deferlayout="true"/> 1.540 + </xul:vbox> 1.541 + </content> 1.542 + 1.543 + <implementation implements="nsIAutoCompletePopup, nsIObserver"> 1.544 + <constructor> 1.545 + <![CDATA[ 1.546 + this.hidden = true; 1.547 + Services.obs.addObserver(this, "browser-search-engine-modified", false); 1.548 + 1.549 + this._results.controller = this; 1.550 + this._searches.controller = this; 1.551 + ]]> 1.552 + </constructor> 1.553 + 1.554 + <destructor> 1.555 + <![CDATA[ 1.556 + Services.obs.removeObserver(this, "browser-search-engine-modified"); 1.557 + ]]> 1.558 + </destructor> 1.559 + 1.560 + <!-- nsIAutocompleteInput --> 1.561 + 1.562 + <field name="_input">null</field> 1.563 + 1.564 + <property name="input" readonly="true" onget="return this._input;"/> 1.565 + <property name="matchCount" readonly="true" onget="return this.input.controller.matchCount;"/> 1.566 + <property name="popupOpen" readonly="true" onget="return !this.hidden"/> 1.567 + <property name="overrideValue" readonly="true" onget="return null;"/> 1.568 + 1.569 + <property name="selectedItem"> 1.570 + <getter> 1.571 + <![CDATA[ 1.572 + return this._isGridBound(this._results) ? this._results.selectedItem : null; 1.573 + ]]> 1.574 + </getter> 1.575 + <setter> 1.576 + <![CDATA[ 1.577 + if (this._isGridBound(this._results)) { 1.578 + this._results.selectedItem = val; 1.579 + } 1.580 + ]]> 1.581 + </setter> 1.582 + </property> 1.583 + 1.584 + <property name="selectedIndex"> 1.585 + <getter> 1.586 + <![CDATA[ 1.587 + return this._isGridBound(this._results) ? this._results.selectedIndex : -1; 1.588 + ]]> 1.589 + </getter> 1.590 + <setter> 1.591 + <![CDATA[ 1.592 + if (this._isGridBound(this._results)) { 1.593 + this._results.selectedIndex = val; 1.594 + } 1.595 + ]]> 1.596 + </setter> 1.597 + </property> 1.598 + 1.599 + <property name="hasSelection"> 1.600 + <getter> 1.601 + <![CDATA[ 1.602 + if (!this._isGridBound(this._results) || 1.603 + !this._isGridBound(this._searches)) 1.604 + return false; 1.605 + 1.606 + return (this._results.selectedIndex >= 0 || 1.607 + this._searches.selectedIndex >= 0); 1.608 + ]]> 1.609 + </getter> 1.610 + </property> 1.611 + 1.612 + <method name="openAutocompletePopup"> 1.613 + <parameter name="aInput"/> 1.614 + <parameter name="aElement"/> 1.615 + <body> 1.616 + <![CDATA[ 1.617 + if (this.popupOpen) 1.618 + return; 1.619 + 1.620 + ContextUI.dismissContextAppbar(); 1.621 + 1.622 + this._input = aInput; 1.623 + this._grid = this._results; 1.624 + 1.625 + this.clearSelection(); 1.626 + this.invalidate(); 1.627 + 1.628 + let viewstate = this.getAttribute("viewstate"); 1.629 + switch (viewstate) { 1.630 + case "portrait": 1.631 + case "snapped": 1.632 + this._results.setAttribute("vertical", true); 1.633 + break; 1.634 + default: 1.635 + this._results.removeAttribute("vertical"); 1.636 + break; 1.637 + } 1.638 + 1.639 + this._results.arrangeItemsNow(); 1.640 + this._searches.arrangeItemsNow(); 1.641 + 1.642 + this.hidden = false; 1.643 + Elements.urlbarState.setAttribute("autocomplete", "true"); 1.644 + ]]> 1.645 + </body> 1.646 + </method> 1.647 + 1.648 + <method name="closePopup"> 1.649 + <body> 1.650 + <![CDATA[ 1.651 + if (!this.popupOpen) 1.652 + return; 1.653 + 1.654 + this.input.controller.stopSearch(); 1.655 + this.hidden = true; 1.656 + Elements.urlbarState.removeAttribute("autocomplete"); 1.657 + ]]> 1.658 + </body> 1.659 + </method> 1.660 + 1.661 + <!-- Updating grid content --> 1.662 + 1.663 + <field name="_grid">null</field> 1.664 + 1.665 + <field name="_results" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results');</field> 1.666 + <field name="_resultsContainer" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results-container');</field> 1.667 + 1.668 + <field name="_searchesHeader" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches-header');</field> 1.669 + <field name="_searches" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches');</field> 1.670 + 1.671 + <property name="_otherGrid" readonly="true"> 1.672 + <getter> 1.673 + <![CDATA[ 1.674 + if (this._grid == null) 1.675 + return null; 1.676 + 1.677 + return (this._grid == this._results) ? this._searches : this._results; 1.678 + ]]> 1.679 + </getter> 1.680 + </property> 1.681 + 1.682 + <method name="_isGridBound"> 1.683 + <parameter name="aGrid"/> 1.684 + <body> 1.685 + <![CDATA[ 1.686 + return aGrid && aGrid.isBound; 1.687 + ]]> 1.688 + </body> 1.689 + </method> 1.690 + 1.691 + <method name="invalidate"> 1.692 + <body> 1.693 + <![CDATA[ 1.694 + if (!this.popupOpen) 1.695 + return; 1.696 + 1.697 + this.updateSearchEngineHeader(); 1.698 + this.updateResults(); 1.699 + ]]> 1.700 + </body> 1.701 + </method> 1.702 + 1.703 + <!-- Updating grid content: results --> 1.704 + 1.705 + <method name="updateResults"> 1.706 + <body> 1.707 + <![CDATA[ 1.708 + if (!this._isGridBound(this._results)) 1.709 + return; 1.710 + 1.711 + if (!this.input) 1.712 + return; 1.713 + 1.714 + let haveNoResults = (this.matchCount == 0); 1.715 + 1.716 + if (haveNoResults) { 1.717 + this._results.clearAll(); 1.718 + this.setAttribute("nomatch", true); 1.719 + this._resultsContainer.removeAttribute("flex"); 1.720 + return; 1.721 + } 1.722 + 1.723 + this.removeAttribute("nomatch"); 1.724 + this._resultsContainer.setAttribute("flex", 1); 1.725 + 1.726 + let controller = this.input.controller; 1.727 + let lastMatch = this.matchCount - 1; 1.728 + let iterCount = Math.max(this._results.itemCount, this.matchCount); 1.729 + 1.730 + // Swap out existing items for new search hit results 1.731 + for (let i = 0; i < iterCount; i++) { 1.732 + let item = this._results._slotAt(i); 1.733 + if (i > lastMatch) { 1.734 + item.removeAttribute("value"); 1.735 + item.removeAttribute("autocomplete"); 1.736 + continue; 1.737 + } 1.738 + 1.739 + let value = controller.getValueAt(i); 1.740 + let label = controller.getCommentAt(i) || value; 1.741 + let iconURI = controller.getImageAt(i); 1.742 + 1.743 + item.setAttribute("autocomplete", true); 1.744 + item.setAttribute("label", label); 1.745 + item.setAttribute("value", value); 1.746 + item.setAttribute("iconURI", iconURI); 1.747 + let xpFaviconURI = Services.io.newURI(iconURI.replace("moz-anno:favicon:",""), null, null); 1.748 + Task.spawn(function() { 1.749 + let colorInfo = yield ColorUtils.getForegroundAndBackgroundIconColors(xpFaviconURI); 1.750 + if ( !(colorInfo && colorInfo.background && colorInfo.foreground) 1.751 + || (item.getAttribute("iconURI") != iconURI) ) { 1.752 + return; 1.753 + } 1.754 + let { background, foreground } = colorInfo; 1.755 + item.style.color = foreground; //color text 1.756 + item.setAttribute("customColor", background); 1.757 + // when bound, use the setter to propogate the color change through the tile 1.758 + if ('color' in item) { 1.759 + item.color = background; 1.760 + } 1.761 + }).then(null, err => Components.utils.reportError(err)); 1.762 + } 1.763 + 1.764 + this._results.arrangeItems(); 1.765 + ]]> 1.766 + </body> 1.767 + </method> 1.768 + 1.769 + <!-- Updating grid content: search engines --> 1.770 + 1.771 + <field name="_engines">[]</field> 1.772 + 1.773 + <method name="_initSearchEngines"> 1.774 + <body> 1.775 + <![CDATA[ 1.776 + Services.search.init(this.updateSearchEngineGrid.bind(this)); 1.777 + ]]> 1.778 + </body> 1.779 + </method> 1.780 + 1.781 + <method name="updateSearchEngineGrid"> 1.782 + <body> 1.783 + <![CDATA[ 1.784 + if (!this._isGridBound(this._searches)) 1.785 + return; 1.786 + 1.787 + this._engines = Services.search.getVisibleEngines(); 1.788 + 1.789 + while (this._searches.itemCount > 0) 1.790 + this._searches.removeItemAt(0, true); 1.791 + 1.792 + this._engines.forEach(function (anEngine) { 1.793 + let item = this._searches.appendItem("", anEngine.name, true); 1.794 + item.setAttribute("autocomplete", "true"); 1.795 + item.setAttribute("search", "true"); 1.796 + 1.797 + let largeImage = anEngine.getIconURLBySize(74,74); 1.798 + if (largeImage) { 1.799 + item.setAttribute("iconsize", "large"); 1.800 + item.setAttribute("iconURI", largeImage); 1.801 + } else if (anEngine.iconURI && anEngine.iconURI.spec) { 1.802 + item.setAttribute("iconURI", anEngine.iconURI.spec); 1.803 + item.setAttribute("customColor", "#fff"); 1.804 + } 1.805 + }.bind(this)); 1.806 + 1.807 + this._searches.arrangeItems(); 1.808 + ]]> 1.809 + </body> 1.810 + </method> 1.811 + 1.812 + <method name="updateSearchEngineHeader"> 1.813 + <body> 1.814 + <![CDATA[ 1.815 + if (!this._isGridBound(this._searches)) 1.816 + return; 1.817 + 1.818 + let searchString = this.input.controller.searchString; 1.819 + let label = Strings.browser.formatStringFromName( 1.820 + "opensearch.search.header", [searchString], 1); 1.821 + 1.822 + this._searchesHeader.value = label; 1.823 + ]]> 1.824 + </body> 1.825 + </method> 1.826 + 1.827 + <!-- Selecting results --> 1.828 + 1.829 + <method name="selectBy"> 1.830 + <parameter name="aReverse"/> 1.831 + <parameter name="aPage"/> 1.832 + <body> 1.833 + <![CDATA[ 1.834 + if (!this._isGridBound(this._results) || 1.835 + !this._isGridBound(this._searches)) 1.836 + return; 1.837 + 1.838 + // Move between grids if we're at the edge of one. 1.839 + if ((this._grid.isSelectionAtEnd && !aReverse) || 1.840 + (this._grid.isSelectionAtStart && aReverse)) { 1.841 + let index = !aReverse ? 0 : this._otherGrid.itemCount - 1; 1.842 + this._otherGrid.selectedIndex = index; 1.843 + } else { 1.844 + this._grid.offsetSelection(aReverse ? -1 : 1); 1.845 + } 1.846 + ]]> 1.847 + </body> 1.848 + </method> 1.849 + 1.850 + <method name="clearSelection"> 1.851 + <body> 1.852 + <![CDATA[ 1.853 + if (this._isGridBound(this._results)) 1.854 + this._results.clearSelection(); 1.855 + 1.856 + if (this._isGridBound(this._searches)) 1.857 + this._searches.clearSelection(); 1.858 + ]]> 1.859 + </body> 1.860 + </method> 1.861 + 1.862 + <!-- Submitting selected results --> 1.863 + 1.864 + <method name="submitSelected"> 1.865 + <body> 1.866 + <![CDATA[ 1.867 + if (this._isGridBound(this._results) && 1.868 + this._results.selectedIndex >= 0) { 1.869 + let url = this.input.controller.getValueAt(this._results.selectedIndex); 1.870 + this.input.value = url; 1.871 + return this.input.submitURL(); 1.872 + } 1.873 + 1.874 + if (this._isGridBound(this._searches) && 1.875 + this._searches.selectedIndex >= 0) { 1.876 + let engine = this._engines[this._searches.selectedIndex]; 1.877 + return this.input.submitSearch(engine.name); 1.878 + } 1.879 + 1.880 + return false; 1.881 + ]]> 1.882 + </body> 1.883 + </method> 1.884 + 1.885 + <method name="handleItemClick"> 1.886 + <parameter name="aItem"/> 1.887 + <parameter name="aEvent"/> 1.888 + <body> 1.889 + <![CDATA[ 1.890 + this.submitSelected(); 1.891 + ]]> 1.892 + </body> 1.893 + </method> 1.894 + 1.895 + <!-- nsIObserver --> 1.896 + 1.897 + <method name="observe"> 1.898 + <parameter name="aSubject"/> 1.899 + <parameter name="aTopic"/> 1.900 + <parameter name="aData"/> 1.901 + <body> 1.902 + <![CDATA[ 1.903 + switch (aTopic) { 1.904 + case "browser-search-engine-modified": 1.905 + this.updateSearchEngineGrid(); 1.906 + break; 1.907 + } 1.908 + ]]> 1.909 + </body> 1.910 + </method> 1.911 + </implementation> 1.912 + 1.913 + <handlers> 1.914 + <handler event="contentgenerated"> 1.915 + <![CDATA[ 1.916 + let grid = event.originalTarget; 1.917 + 1.918 + if (grid == this._searches) 1.919 + this._initSearchEngines(); 1.920 + 1.921 + if (grid == this._results) 1.922 + this.updateResults(); 1.923 + ]]> 1.924 + </handler> 1.925 + 1.926 + <handler event="select"> 1.927 + <![CDATA[ 1.928 + let grid = event.originalTarget; 1.929 + 1.930 + // If a selection was made on a different grid, 1.931 + // remove selection from the current grid. 1.932 + if (grid != this._grid) { 1.933 + this._grid.clearSelection(); 1.934 + this._grid = this._otherGrid; 1.935 + } 1.936 + ]]> 1.937 + </handler> 1.938 + </handlers> 1.939 + </binding> 1.940 +</bindings>