1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/urlbarBindings.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2241 @@ 1.4 +<?xml version="1.0"?> 1.5 + 1.6 +# -*- Mode: HTML -*- 1.7 +# This Source Code Form is subject to the terms of the Mozilla Public 1.8 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.10 + 1.11 +<!DOCTYPE bindings [ 1.12 +<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> 1.13 +%notificationDTD; 1.14 +<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> 1.15 +%browserDTD; 1.16 +]> 1.17 + 1.18 +<bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl" 1.19 + xmlns:html="http://www.w3.org/1999/xhtml" 1.20 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 1.21 + xmlns:xbl="http://www.mozilla.org/xbl"> 1.22 + 1.23 + <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> 1.24 + 1.25 + <content sizetopopup="pref"> 1.26 + <xul:hbox anonid="textbox-container" 1.27 + class="autocomplete-textbox-container urlbar-textbox-container" 1.28 + flex="1" xbl:inherits="focused"> 1.29 + <children includes="image|deck|stack|box"> 1.30 + <xul:image class="autocomplete-icon" allowevents="true"/> 1.31 + </children> 1.32 + <xul:hbox anonid="textbox-input-box" 1.33 + class="textbox-input-box urlbar-input-box" 1.34 + flex="1" xbl:inherits="tooltiptext=inputtooltiptext"> 1.35 + <children/> 1.36 + <html:input anonid="input" 1.37 + class="autocomplete-textbox urlbar-input textbox-input uri-element-right-align" 1.38 + allowevents="true" 1.39 + xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/> 1.40 + </xul:hbox> 1.41 + <children includes="hbox"/> 1.42 + </xul:hbox> 1.43 + <xul:dropmarker anonid="historydropmarker" 1.44 + class="autocomplete-history-dropmarker urlbar-history-dropmarker" 1.45 + allowevents="true" 1.46 + xbl:inherits="open,enablehistory,parentfocused=focused"/> 1.47 + <xul:popupset anonid="popupset" 1.48 + class="autocomplete-result-popupset"/> 1.49 + <children includes="toolbarbutton"/> 1.50 + </content> 1.51 + 1.52 + <implementation implements="nsIObserver, nsIDOMEventListener"> 1.53 + <constructor><![CDATA[ 1.54 + this._prefs = Components.classes["@mozilla.org/preferences-service;1"] 1.55 + .getService(Components.interfaces.nsIPrefService) 1.56 + .getBranch("browser.urlbar."); 1.57 + 1.58 + this._prefs.addObserver("", this, false); 1.59 + this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll"); 1.60 + this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll"); 1.61 + this.completeDefaultIndex = this._prefs.getBoolPref("autoFill"); 1.62 + this.timeout = this._prefs.getIntPref("delay"); 1.63 + this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled"); 1.64 + this._mayTrimURLs = this._prefs.getBoolPref("trimURLs"); 1.65 + this._ignoreNextSelect = false; 1.66 + 1.67 + this.inputField.controllers.insertControllerAt(0, this._copyCutController); 1.68 + this.inputField.addEventListener("mousedown", this, false); 1.69 + this.inputField.addEventListener("mousemove", this, false); 1.70 + this.inputField.addEventListener("mouseout", this, false); 1.71 + this.inputField.addEventListener("overflow", this, false); 1.72 + this.inputField.addEventListener("underflow", this, false); 1.73 + 1.74 + try { 1.75 + if (this._prefs.getBoolPref("unifiedcomplete")) { 1.76 + this.setAttribute("autocompletesearch", "unifiedcomplete"); 1.77 + } 1.78 + } catch (ex) {} 1.79 + 1.80 + const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.81 + var textBox = document.getAnonymousElementByAttribute(this, 1.82 + "anonid", "textbox-input-box"); 1.83 + var cxmenu = document.getAnonymousElementByAttribute(textBox, 1.84 + "anonid", "input-box-contextmenu"); 1.85 + var pasteAndGo; 1.86 + cxmenu.addEventListener("popupshowing", function() { 1.87 + if (!pasteAndGo) 1.88 + return; 1.89 + var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); 1.90 + var enabled = controller.isCommandEnabled("cmd_paste"); 1.91 + if (enabled) 1.92 + pasteAndGo.removeAttribute("disabled"); 1.93 + else 1.94 + pasteAndGo.setAttribute("disabled", "true"); 1.95 + }, false); 1.96 + 1.97 + var insertLocation = cxmenu.firstChild; 1.98 + while (insertLocation.nextSibling && 1.99 + insertLocation.getAttribute("cmd") != "cmd_paste") 1.100 + insertLocation = insertLocation.nextSibling; 1.101 + if (insertLocation) { 1.102 + pasteAndGo = document.createElement("menuitem"); 1.103 + let label = Services.strings.createBundle("chrome://browser/locale/browser.properties"). 1.104 + GetStringFromName("pasteAndGo.label"); 1.105 + pasteAndGo.setAttribute("label", label); 1.106 + pasteAndGo.setAttribute("anonid", "paste-and-go"); 1.107 + pasteAndGo.setAttribute("oncommand", 1.108 + "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();"); 1.109 + cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling); 1.110 + } 1.111 + ]]></constructor> 1.112 + 1.113 + <destructor><![CDATA[ 1.114 + this._prefs.removeObserver("", this); 1.115 + this._prefs = null; 1.116 + this.inputField.controllers.removeController(this._copyCutController); 1.117 + this.inputField.removeEventListener("mousedown", this, false); 1.118 + this.inputField.removeEventListener("mousemove", this, false); 1.119 + this.inputField.removeEventListener("mouseout", this, false); 1.120 + this.inputField.removeEventListener("overflow", this, false); 1.121 + this.inputField.removeEventListener("underflow", this, false); 1.122 + ]]></destructor> 1.123 + 1.124 + <field name="_value"></field> 1.125 + 1.126 + <!-- 1.127 + onBeforeValueGet is called by the base-binding's .value getter. 1.128 + It can return an object with a "value" property, to override the 1.129 + return value of the getter. 1.130 + --> 1.131 + <method name="onBeforeValueGet"> 1.132 + <body><![CDATA[ 1.133 + if (this.hasAttribute("actiontype")) 1.134 + return {value: this._value}; 1.135 + return null; 1.136 + ]]></body> 1.137 + </method> 1.138 + 1.139 + <!-- 1.140 + onBeforeValueSet is called by the base-binding's .value setter. 1.141 + It should return the value that the setter should use. 1.142 + --> 1.143 + <method name="onBeforeValueSet"> 1.144 + <parameter name="aValue"/> 1.145 + <body><![CDATA[ 1.146 + this._value = aValue; 1.147 + var returnValue = aValue; 1.148 + var action = this._parseActionUrl(aValue); 1.149 + // Don't put back the action if we are invoked while override actions 1.150 + // is active. 1.151 + if (action && this._numNoActionsKeys <= 0) { 1.152 + returnValue = action.param; 1.153 + this.setAttribute("actiontype", action.type); 1.154 + } else { 1.155 + this.removeAttribute("actiontype"); 1.156 + } 1.157 + return returnValue; 1.158 + ]]></body> 1.159 + </method> 1.160 + 1.161 + <field name="_mayTrimURLs">true</field> 1.162 + <method name="trimValue"> 1.163 + <parameter name="aURL"/> 1.164 + <body><![CDATA[ 1.165 + // This method must not modify the given URL such that calling 1.166 + // nsIURIFixup::createFixupURI with the result will produce a different URI. 1.167 + return this._mayTrimURLs ? trimURL(aURL) : aURL; 1.168 + ]]></body> 1.169 + </method> 1.170 + 1.171 + <field name="_formattingEnabled">true</field> 1.172 + <method name="formatValue"> 1.173 + <body><![CDATA[ 1.174 + if (!this._formattingEnabled || this.focused) 1.175 + return; 1.176 + 1.177 + let controller = this.editor.selectionController; 1.178 + let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); 1.179 + selection.removeAllRanges(); 1.180 + 1.181 + let textNode = this.editor.rootElement.firstChild; 1.182 + let value = textNode.textContent; 1.183 + 1.184 + let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/); 1.185 + if (protocol && 1.186 + ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1) 1.187 + return; 1.188 + let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/); 1.189 + if (!matchedURL) 1.190 + return; 1.191 + 1.192 + let [, preDomain, domain] = matchedURL; 1.193 + let baseDomain = domain; 1.194 + let subDomain = ""; 1.195 + // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159) 1.196 + if (domain[0] != "[") { 1.197 + try { 1.198 + baseDomain = Services.eTLD.getBaseDomainFromHost(domain); 1.199 + if (!domain.endsWith(baseDomain)) { 1.200 + // getBaseDomainFromHost converts its resultant to ACE. 1.201 + let IDNService = Cc["@mozilla.org/network/idn-service;1"] 1.202 + .getService(Ci.nsIIDNService); 1.203 + baseDomain = IDNService.convertACEtoUTF8(baseDomain); 1.204 + } 1.205 + } catch (e) {} 1.206 + } 1.207 + if (baseDomain != domain) { 1.208 + subDomain = domain.slice(0, -baseDomain.length); 1.209 + } 1.210 + 1.211 + let rangeLength = preDomain.length + subDomain.length; 1.212 + if (rangeLength) { 1.213 + let range = document.createRange(); 1.214 + range.setStart(textNode, 0); 1.215 + range.setEnd(textNode, rangeLength); 1.216 + selection.addRange(range); 1.217 + } 1.218 + 1.219 + let startRest = preDomain.length + domain.length; 1.220 + if (startRest < value.length) { 1.221 + let range = document.createRange(); 1.222 + range.setStart(textNode, startRest); 1.223 + range.setEnd(textNode, value.length); 1.224 + selection.addRange(range); 1.225 + } 1.226 + ]]></body> 1.227 + </method> 1.228 + 1.229 + <method name="_clearFormatting"> 1.230 + <body><![CDATA[ 1.231 + if (!this._formattingEnabled) 1.232 + return; 1.233 + 1.234 + let controller = this.editor.selectionController; 1.235 + let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); 1.236 + selection.removeAllRanges(); 1.237 + ]]></body> 1.238 + </method> 1.239 + 1.240 + <method name="handleRevert"> 1.241 + <body><![CDATA[ 1.242 + var isScrolling = this.popupOpen; 1.243 + 1.244 + gBrowser.userTypedValue = null; 1.245 + 1.246 + // don't revert to last valid url unless page is NOT loading 1.247 + // and user is NOT key-scrolling through autocomplete list 1.248 + if (!XULBrowserWindow.isBusy && !isScrolling) { 1.249 + URLBarSetURI(); 1.250 + 1.251 + // If the value isn't empty and the urlbar has focus, select the value. 1.252 + if (this.value && this.hasAttribute("focused")) 1.253 + this.select(); 1.254 + } 1.255 + 1.256 + // tell widget to revert to last typed text only if the user 1.257 + // was scrolling when they hit escape 1.258 + return !isScrolling; 1.259 + ]]></body> 1.260 + </method> 1.261 + 1.262 + <method name="handleCommand"> 1.263 + <parameter name="aTriggeringEvent"/> 1.264 + <body><![CDATA[ 1.265 + if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2) 1.266 + return; // Do nothing for right clicks 1.267 + 1.268 + var url = this.value; 1.269 + var mayInheritPrincipal = false; 1.270 + var postData = null; 1.271 + 1.272 + var action = this._parseActionUrl(url); 1.273 + let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange; 1.274 + 1.275 + let matchLastLocationChange = true; 1.276 + if (action) { 1.277 + url = action.param; 1.278 + if (this.hasAttribute("actiontype")) { 1.279 + if (action.type == "switchtab") { 1.280 + this.handleRevert(); 1.281 + let prevTab = gBrowser.selectedTab; 1.282 + if (switchToTabHavingURI(url) && 1.283 + isTabEmpty(prevTab)) 1.284 + gBrowser.removeTab(prevTab); 1.285 + } 1.286 + return; 1.287 + } 1.288 + continueOperation.call(this); 1.289 + } 1.290 + else { 1.291 + this._canonizeURL(aTriggeringEvent, response => { 1.292 + [url, postData, mayInheritPrincipal] = response; 1.293 + if (url) { 1.294 + matchLastLocationChange = (lastLocationChange == 1.295 + gBrowser.selectedBrowser.lastLocationChange); 1.296 + continueOperation.call(this); 1.297 + } 1.298 + }); 1.299 + } 1.300 + 1.301 + function continueOperation() 1.302 + { 1.303 + this.value = url; 1.304 + gBrowser.userTypedValue = url; 1.305 + try { 1.306 + addToUrlbarHistory(url); 1.307 + } catch (ex) { 1.308 + // Things may go wrong when adding url to session history, 1.309 + // but don't let that interfere with the loading of the url. 1.310 + Cu.reportError(ex); 1.311 + } 1.312 + 1.313 + function loadCurrent() { 1.314 + let webnav = Ci.nsIWebNavigation; 1.315 + let flags = webnav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | 1.316 + webnav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; 1.317 + // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from 1.318 + // inheriting the currently loaded document's principal, unless this 1.319 + // URL is marked as safe to inherit (e.g. came from a bookmark 1.320 + // keyword). 1.321 + if (!mayInheritPrincipal) 1.322 + flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER; 1.323 + gBrowser.loadURIWithFlags(url, flags, null, null, postData); 1.324 + } 1.325 + 1.326 + // Focus the content area before triggering loads, since if the load 1.327 + // occurs in a new tab, we want focus to be restored to the content 1.328 + // area when the current tab is re-selected. 1.329 + gBrowser.selectedBrowser.focus(); 1.330 + 1.331 + let isMouseEvent = aTriggeringEvent instanceof MouseEvent; 1.332 + let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey; 1.333 + 1.334 + if (altEnter) { 1.335 + // XXX This was added a long time ago, and I'm not sure why it is 1.336 + // necessary. Alt+Enter's default action might cause a system beep, 1.337 + // or something like that? 1.338 + aTriggeringEvent.preventDefault(); 1.339 + aTriggeringEvent.stopPropagation(); 1.340 + } 1.341 + 1.342 + // If the current tab is empty, ignore Alt+Enter (just reuse this tab) 1.343 + altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab); 1.344 + 1.345 + if (isMouseEvent || altEnter) { 1.346 + // Use the standard UI link behaviors for clicks or Alt+Enter 1.347 + let where = "tab"; 1.348 + if (isMouseEvent) 1.349 + where = whereToOpenLink(aTriggeringEvent, false, false); 1.350 + 1.351 + if (where == "current") { 1.352 + if (matchLastLocationChange) { 1.353 + loadCurrent(); 1.354 + } 1.355 + } else { 1.356 + this.handleRevert(); 1.357 + let params = { allowThirdPartyFixup: true, 1.358 + postData: postData, 1.359 + initiatingDoc: document }; 1.360 + openUILinkIn(url, where, params); 1.361 + } 1.362 + } else { 1.363 + if (matchLastLocationChange) { 1.364 + loadCurrent(); 1.365 + } 1.366 + } 1.367 + } 1.368 + ]]></body> 1.369 + </method> 1.370 + 1.371 + <method name="_canonizeURL"> 1.372 + <parameter name="aTriggeringEvent"/> 1.373 + <parameter name="aCallback"/> 1.374 + <body><![CDATA[ 1.375 + var url = this.value; 1.376 + if (!url) { 1.377 + aCallback(["", null, false]); 1.378 + return; 1.379 + } 1.380 + 1.381 + // Only add the suffix when the URL bar value isn't already "URL-like", 1.382 + // and only if we get a keyboard event, to match user expectations. 1.383 + if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(url) && 1.384 + (aTriggeringEvent instanceof KeyEvent)) { 1.385 +#ifdef XP_MACOSX 1.386 + let accel = aTriggeringEvent.metaKey; 1.387 +#else 1.388 + let accel = aTriggeringEvent.ctrlKey; 1.389 +#endif 1.390 + let shift = aTriggeringEvent.shiftKey; 1.391 + 1.392 + let suffix = ""; 1.393 + 1.394 + switch (true) { 1.395 + case (accel && shift): 1.396 + suffix = ".org/"; 1.397 + break; 1.398 + case (shift): 1.399 + suffix = ".net/"; 1.400 + break; 1.401 + case (accel): 1.402 + try { 1.403 + suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix"); 1.404 + if (suffix.charAt(suffix.length - 1) != "/") 1.405 + suffix += "/"; 1.406 + } catch(e) { 1.407 + suffix = ".com/"; 1.408 + } 1.409 + break; 1.410 + } 1.411 + 1.412 + if (suffix) { 1.413 + // trim leading/trailing spaces (bug 233205) 1.414 + url = url.trim(); 1.415 + 1.416 + // Tack www. and suffix on. If user has appended directories, insert 1.417 + // suffix before them (bug 279035). Be careful not to get two slashes. 1.418 + 1.419 + let firstSlash = url.indexOf("/"); 1.420 + 1.421 + if (firstSlash >= 0) { 1.422 + url = url.substring(0, firstSlash) + suffix + 1.423 + url.substring(firstSlash + 1); 1.424 + } else { 1.425 + url = url + suffix; 1.426 + } 1.427 + 1.428 + url = "http://www." + url; 1.429 + } 1.430 + } 1.431 + 1.432 + getShortcutOrURIAndPostData(url, data => { 1.433 + aCallback([data.url, data.postData, data.mayInheritPrincipal]); 1.434 + }); 1.435 + ]]></body> 1.436 + </method> 1.437 + 1.438 + <field name="_contentIsCropped">false</field> 1.439 + 1.440 + <method name="_initURLTooltip"> 1.441 + <body><![CDATA[ 1.442 + if (this.focused || !this._contentIsCropped) 1.443 + return; 1.444 + this.inputField.setAttribute("tooltiptext", this.value); 1.445 + ]]></body> 1.446 + </method> 1.447 + 1.448 + <method name="_hideURLTooltip"> 1.449 + <body><![CDATA[ 1.450 + this.inputField.removeAttribute("tooltiptext"); 1.451 + ]]></body> 1.452 + </method> 1.453 + 1.454 + <method name="onDragOver"> 1.455 + <parameter name="aEvent"/> 1.456 + <body> 1.457 + var types = aEvent.dataTransfer.types; 1.458 + if (types.contains("application/x-moz-file") || 1.459 + types.contains("text/x-moz-url") || 1.460 + types.contains("text/uri-list") || 1.461 + types.contains("text/unicode")) 1.462 + aEvent.preventDefault(); 1.463 + </body> 1.464 + </method> 1.465 + 1.466 + <method name="onDrop"> 1.467 + <parameter name="aEvent"/> 1.468 + <body><![CDATA[ 1.469 + let url = browserDragAndDrop.drop(aEvent, { }) 1.470 + 1.471 + // The URL bar automatically handles inputs with newline characters, 1.472 + // so we can get away with treating text/x-moz-url flavours as text/plain. 1.473 + if (url) { 1.474 + aEvent.preventDefault(); 1.475 + this.value = url; 1.476 + SetPageProxyState("invalid"); 1.477 + this.focus(); 1.478 + try { 1.479 + urlSecurityCheck(url, 1.480 + gBrowser.contentPrincipal, 1.481 + Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); 1.482 + } catch (ex) { 1.483 + return; 1.484 + } 1.485 + this.handleCommand(); 1.486 + } 1.487 + ]]></body> 1.488 + </method> 1.489 + 1.490 + <method name="_getSelectedValueForClipboard"> 1.491 + <body><![CDATA[ 1.492 + // Grab the actual input field's value, not our value, which could include moz-action: 1.493 + var inputVal = this.inputField.value; 1.494 + var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd); 1.495 + 1.496 + // If the selection doesn't start at the beginning or doesn't span the full domain or 1.497 + // the URL bar is modified, nothing else to do here. 1.498 + if (this.selectionStart > 0 || this.valueIsTyped) 1.499 + return selectedVal; 1.500 + // The selection doesn't span the full domain if it doesn't contain a slash and is 1.501 + // followed by some character other than a slash. 1.502 + if (!selectedVal.contains("/")) { 1.503 + let remainder = inputVal.replace(selectedVal, ""); 1.504 + if (remainder != "" && remainder[0] != "/") 1.505 + return selectedVal; 1.506 + } 1.507 + 1.508 + let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup); 1.509 + 1.510 + let uri; 1.511 + try { 1.512 + uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE); 1.513 + } catch (e) {} 1.514 + if (!uri) 1.515 + return selectedVal; 1.516 + 1.517 + // Only copy exposable URIs 1.518 + try { 1.519 + uri = uriFixup.createExposableURI(uri); 1.520 + } catch (ex) {} 1.521 + 1.522 + // If the entire URL is selected, just use the actual loaded URI. 1.523 + if (inputVal == selectedVal) { 1.524 + // ... but only if isn't a javascript: or data: URI, since those 1.525 + // are hard to read when encoded 1.526 + if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) { 1.527 + // Parentheses are known to confuse third-party applications (bug 458565). 1.528 + selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c)); 1.529 + } 1.530 + 1.531 + return selectedVal; 1.532 + } 1.533 + 1.534 + // Just the beginning of the URL is selected, check for a trimmed 1.535 + // value 1.536 + let spec = uri.spec; 1.537 + let trimmedSpec = this.trimValue(spec); 1.538 + if (spec != trimmedSpec) { 1.539 + // Prepend the portion that trimValue removed from the beginning. 1.540 + // This assumes trimValue will only truncate the URL at 1.541 + // the beginning or end (or both). 1.542 + let trimmedSegments = spec.split(trimmedSpec); 1.543 + selectedVal = trimmedSegments[0] + selectedVal; 1.544 + } 1.545 + 1.546 + return selectedVal; 1.547 + ]]></body> 1.548 + </method> 1.549 + 1.550 + <field name="_copyCutController"><![CDATA[ 1.551 + ({ 1.552 + urlbar: this, 1.553 + doCommand: function(aCommand) { 1.554 + var urlbar = this.urlbar; 1.555 + var val = urlbar._getSelectedValueForClipboard(); 1.556 + if (!val) 1.557 + return; 1.558 + 1.559 + if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) { 1.560 + let start = urlbar.selectionStart; 1.561 + let end = urlbar.selectionEnd; 1.562 + urlbar.inputField.value = urlbar.inputField.value.substring(0, start) + 1.563 + urlbar.inputField.value.substring(end); 1.564 + urlbar.selectionStart = urlbar.selectionEnd = start; 1.565 + urlbar.removeAttribute("actiontype"); 1.566 + SetPageProxyState("invalid"); 1.567 + } 1.568 + 1.569 + Cc["@mozilla.org/widget/clipboardhelper;1"] 1.570 + .getService(Ci.nsIClipboardHelper) 1.571 + .copyString(val, document); 1.572 + }, 1.573 + supportsCommand: function(aCommand) { 1.574 + switch (aCommand) { 1.575 + case "cmd_copy": 1.576 + case "cmd_cut": 1.577 + return true; 1.578 + } 1.579 + return false; 1.580 + }, 1.581 + isCommandEnabled: function(aCommand) { 1.582 + return this.supportsCommand(aCommand) && 1.583 + (aCommand != "cmd_cut" || !this.urlbar.readOnly) && 1.584 + this.urlbar.selectionStart < this.urlbar.selectionEnd; 1.585 + }, 1.586 + onEvent: function(aEventName) {} 1.587 + }) 1.588 + ]]></field> 1.589 + 1.590 + <method name="observe"> 1.591 + <parameter name="aSubject"/> 1.592 + <parameter name="aTopic"/> 1.593 + <parameter name="aData"/> 1.594 + <body><![CDATA[ 1.595 + if (aTopic == "nsPref:changed") { 1.596 + switch (aData) { 1.597 + case "clickSelectsAll": 1.598 + case "doubleClickSelectsAll": 1.599 + this[aData] = this._prefs.getBoolPref(aData); 1.600 + break; 1.601 + case "autoFill": 1.602 + this.completeDefaultIndex = this._prefs.getBoolPref(aData); 1.603 + break; 1.604 + case "delay": 1.605 + this.timeout = this._prefs.getIntPref(aData); 1.606 + break; 1.607 + case "formatting.enabled": 1.608 + this._formattingEnabled = this._prefs.getBoolPref(aData); 1.609 + break; 1.610 + case "trimURLs": 1.611 + this._mayTrimURLs = this._prefs.getBoolPref(aData); 1.612 + break; 1.613 + case "unifiedcomplete": 1.614 + let useUnifiedComplete = false; 1.615 + try { 1.616 + useUnifiedComplete = this._prefs.getBoolPref(aData); 1.617 + } catch (ex) {} 1.618 + this.setAttribute("autocompletesearch", 1.619 + useUnifiedComplete ? "unifiedcomplete" 1.620 + : "urlinline history"); 1.621 + } 1.622 + } 1.623 + ]]></body> 1.624 + </method> 1.625 + 1.626 + <method name="handleEvent"> 1.627 + <parameter name="aEvent"/> 1.628 + <body><![CDATA[ 1.629 + switch (aEvent.type) { 1.630 + case "mousedown": 1.631 + if (this.doubleClickSelectsAll && 1.632 + aEvent.button == 0 && aEvent.detail == 2) { 1.633 + this.editor.selectAll(); 1.634 + aEvent.preventDefault(); 1.635 + } 1.636 + break; 1.637 + case "mousemove": 1.638 + this._initURLTooltip(); 1.639 + break; 1.640 + case "mouseout": 1.641 + this._hideURLTooltip(); 1.642 + break; 1.643 + case "overflow": 1.644 + this._contentIsCropped = true; 1.645 + break; 1.646 + case "underflow": 1.647 + this._contentIsCropped = false; 1.648 + this._hideURLTooltip(); 1.649 + break; 1.650 + } 1.651 + ]]></body> 1.652 + </method> 1.653 + 1.654 + <property name="textValue" 1.655 + onget="return this.value;"> 1.656 + <setter> 1.657 + <![CDATA[ 1.658 + try { 1.659 + val = losslessDecodeURI(makeURI(val)); 1.660 + } catch (ex) { } 1.661 + 1.662 + // Trim popup selected values, but never trim results coming from 1.663 + // autofill. 1.664 + if (this.popup.selectedIndex == -1) 1.665 + this._disableTrim = true; 1.666 + this.value = val; 1.667 + this._disableTrim = false; 1.668 + 1.669 + // Completing a result should simulate the user typing the result, so 1.670 + // fire an input event. 1.671 + let evt = document.createEvent("UIEvents"); 1.672 + evt.initUIEvent("input", true, false, window, 0); 1.673 + this.mIgnoreInput = true; 1.674 + this.dispatchEvent(evt); 1.675 + this.mIgnoreInput = false; 1.676 + 1.677 + return this.value; 1.678 + ]]> 1.679 + </setter> 1.680 + </property> 1.681 + 1.682 + <method name="_parseActionUrl"> 1.683 + <parameter name="aUrl"/> 1.684 + <body><![CDATA[ 1.685 + if (!aUrl.startsWith("moz-action:")) 1.686 + return null; 1.687 + 1.688 + // url is in the format moz-action:ACTION,PARAM 1.689 + let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/); 1.690 + return {type: action, param: param}; 1.691 + ]]></body> 1.692 + </method> 1.693 + 1.694 + <field name="_numNoActionsKeys"><![CDATA[ 1.695 + 0 1.696 + ]]></field> 1.697 + 1.698 + <method name="_clearNoActions"> 1.699 + <parameter name="aURL"/> 1.700 + <body><![CDATA[ 1.701 + this._numNoActionsKeys = 0; 1.702 + this.popup.removeAttribute("noactions"); 1.703 + let action = this._parseActionUrl(this._value); 1.704 + if (action) 1.705 + this.setAttribute("actiontype", action.type); 1.706 + ]]></body> 1.707 + </method> 1.708 + 1.709 + <method name="selectTextRange"> 1.710 + <parameter name="aStartIndex"/> 1.711 + <parameter name="aEndIndex"/> 1.712 + <body><![CDATA[ 1.713 + this._ignoreNextSelect = true; 1.714 + this.inputField.setSelectionRange(aStartIndex, aEndIndex); 1.715 + ]]></body> 1.716 + </method> 1.717 + </implementation> 1.718 + 1.719 + <handlers> 1.720 + <handler event="keydown"><![CDATA[ 1.721 + if ((event.keyCode === KeyEvent.DOM_VK_ALT || 1.722 + event.keyCode === KeyEvent.DOM_VK_SHIFT) && 1.723 + this.popup.selectedIndex >= 0) { 1.724 + this._numNoActionsKeys++; 1.725 + this.popup.setAttribute("noactions", "true"); 1.726 + this.removeAttribute("actiontype"); 1.727 + } 1.728 + ]]></handler> 1.729 + 1.730 + <handler event="keyup"><![CDATA[ 1.731 + if ((event.keyCode === KeyEvent.DOM_VK_ALT || 1.732 + event.keyCode === KeyEvent.DOM_VK_SHIFT) && 1.733 + this._numNoActionsKeys > 0) { 1.734 + this._numNoActionsKeys--; 1.735 + if (this._numNoActionsKeys == 0) 1.736 + this._clearNoActions(); 1.737 + } 1.738 + ]]></handler> 1.739 + 1.740 + <handler event="blur"><![CDATA[ 1.741 + this._clearNoActions(); 1.742 + this.formatValue(); 1.743 + ]]></handler> 1.744 + 1.745 + <handler event="dragstart" phase="capturing"><![CDATA[ 1.746 + // Drag only if the gesture starts from the input field. 1.747 + if (event.originalTarget != this.inputField) 1.748 + return; 1.749 + 1.750 + // Drag only if the entire value is selected and it's a valid URI. 1.751 + var isFullSelection = this.selectionStart == 0 && 1.752 + this.selectionEnd == this.textLength; 1.753 + if (!isFullSelection || 1.754 + this.getAttribute("pageproxystate") != "valid") 1.755 + return; 1.756 + 1.757 + var urlString = content.location.href; 1.758 + var title = content.document.title || urlString; 1.759 + var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>"; 1.760 + 1.761 + var dt = event.dataTransfer; 1.762 + dt.setData("text/x-moz-url", urlString + "\n" + title); 1.763 + dt.setData("text/unicode", urlString); 1.764 + dt.setData("text/html", htmlString); 1.765 + 1.766 + dt.effectAllowed = "copyLink"; 1.767 + event.stopPropagation(); 1.768 + ]]></handler> 1.769 + 1.770 + <handler event="focus" phase="capturing"><![CDATA[ 1.771 + this._hideURLTooltip(); 1.772 + this._clearFormatting(); 1.773 + ]]></handler> 1.774 + 1.775 + <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/> 1.776 + <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/> 1.777 + <handler event="select"><![CDATA[ 1.778 + if (this._ignoreNextSelect) { 1.779 + // If this select event is coming from autocomplete's selectTextRange, 1.780 + // then we don't need to adjust what's on the selection keyboard here, 1.781 + // but make sure to reset the flag since this should be a one-time 1.782 + // suppression. 1.783 + this._ignoreNextSelect = false; 1.784 + return; 1.785 + } 1.786 + 1.787 + if (!Cc["@mozilla.org/widget/clipboard;1"] 1.788 + .getService(Ci.nsIClipboard) 1.789 + .supportsSelectionClipboard()) 1.790 + return; 1.791 + 1.792 + var val = this._getSelectedValueForClipboard(); 1.793 + if (!val) 1.794 + return; 1.795 + 1.796 + Cc["@mozilla.org/widget/clipboardhelper;1"] 1.797 + .getService(Ci.nsIClipboardHelper) 1.798 + .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard, document); 1.799 + ]]></handler> 1.800 + </handlers> 1.801 + 1.802 + </binding> 1.803 + 1.804 + <!-- Note: this binding is applied to the autocomplete popup used in the Search bar and in web page content --> 1.805 + <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup"> 1.806 + <implementation> 1.807 + <method name="openAutocompletePopup"> 1.808 + <parameter name="aInput"/> 1.809 + <parameter name="aElement"/> 1.810 + <body> 1.811 + <![CDATA[ 1.812 + // initially the panel is hidden 1.813 + // to avoid impacting startup / new window performance 1.814 + aInput.popup.hidden = false; 1.815 + 1.816 + // this method is defined on the base binding 1.817 + this._openAutocompletePopup(aInput, aElement); 1.818 + ]]></body> 1.819 + </method> 1.820 + 1.821 + <method name="onPopupClick"> 1.822 + <parameter name="aEvent"/> 1.823 + <body><![CDATA[ 1.824 + // Ignore all right-clicks 1.825 + if (aEvent.button == 2) 1.826 + return; 1.827 + 1.828 + var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); 1.829 + 1.830 + // Check for unmodified left-click, and use default behavior 1.831 + if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey && 1.832 + !aEvent.altKey && !aEvent.metaKey) { 1.833 + controller.handleEnter(true); 1.834 + return; 1.835 + } 1.836 + 1.837 + // Check for middle-click or modified clicks on the search bar 1.838 + var searchBar = BrowserSearch.searchBar; 1.839 + if (searchBar && searchBar.textbox == this.mInput) { 1.840 + // Handle search bar popup clicks 1.841 + var search = controller.getValueAt(this.selectedIndex); 1.842 + 1.843 + // close the autocomplete popup and revert the entered search term 1.844 + this.closePopup(); 1.845 + controller.handleEscape(); 1.846 + 1.847 + // Fill in the search bar's value 1.848 + searchBar.value = search; 1.849 + 1.850 + // open the search results according to the clicking subtlety 1.851 + var where = whereToOpenLink(aEvent, false, true); 1.852 + searchBar.doSearch(search, where); 1.853 + } 1.854 + ]]></body> 1.855 + </method> 1.856 + </implementation> 1.857 + </binding> 1.858 + 1.859 + <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup"> 1.860 + <implementation> 1.861 + <field name="_maxResults">0</field> 1.862 + 1.863 + <field name="_bundle" readonly="true"> 1.864 + Cc["@mozilla.org/intl/stringbundle;1"]. 1.865 + getService(Ci.nsIStringBundleService). 1.866 + createBundle("chrome://browser/locale/places/places.properties"); 1.867 + </field> 1.868 + 1.869 + <property name="maxResults" readonly="true"> 1.870 + <getter> 1.871 + <![CDATA[ 1.872 + if (!this._maxResults) { 1.873 + var prefService = 1.874 + Components.classes["@mozilla.org/preferences-service;1"] 1.875 + .getService(Components.interfaces.nsIPrefBranch); 1.876 + this._maxResults = prefService.getIntPref("browser.urlbar.maxRichResults"); 1.877 + } 1.878 + return this._maxResults; 1.879 + ]]> 1.880 + </getter> 1.881 + </property> 1.882 + 1.883 + <method name="openAutocompletePopup"> 1.884 + <parameter name="aInput"/> 1.885 + <parameter name="aElement"/> 1.886 + <body> 1.887 + <![CDATA[ 1.888 + // initially the panel is hidden 1.889 + // to avoid impacting startup / new window performance 1.890 + aInput.popup.hidden = false; 1.891 + 1.892 + // this method is defined on the base binding 1.893 + this._openAutocompletePopup(aInput, aElement); 1.894 + ]]></body> 1.895 + </method> 1.896 + 1.897 + <method name="onPopupClick"> 1.898 + <parameter name="aEvent"/> 1.899 + <body> 1.900 + <![CDATA[ 1.901 + // Ignore right-clicks 1.902 + if (aEvent.button == 2) 1.903 + return; 1.904 + 1.905 + var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); 1.906 + 1.907 + // Check for unmodified left-click, and use default behavior 1.908 + if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey && 1.909 + !aEvent.altKey && !aEvent.metaKey) { 1.910 + controller.handleEnter(true); 1.911 + return; 1.912 + } 1.913 + 1.914 + // Check for middle-click or modified clicks on the URL bar 1.915 + if (gURLBar && this.mInput == gURLBar) { 1.916 + var url = controller.getValueAt(this.selectedIndex); 1.917 + 1.918 + // close the autocomplete popup and revert the entered address 1.919 + this.closePopup(); 1.920 + controller.handleEscape(); 1.921 + 1.922 + // Check if this is meant to be an action 1.923 + let action = this.mInput._parseActionUrl(url); 1.924 + if (action) { 1.925 + if (action.type == "switchtab") 1.926 + url = action.param; 1.927 + else 1.928 + return; 1.929 + } 1.930 + 1.931 + // respect the usual clicking subtleties 1.932 + openUILink(url, aEvent); 1.933 + } 1.934 + ]]> 1.935 + </body> 1.936 + </method> 1.937 + 1.938 + <method name="createResultLabel"> 1.939 + <parameter name="aTitle"/> 1.940 + <parameter name="aUrl"/> 1.941 + <parameter name="aType"/> 1.942 + <body> 1.943 + <![CDATA[ 1.944 + var label = aTitle + " " + aUrl; 1.945 + // convert aType (ex: "ac-result-type-<aType>") to text to be spoke aloud 1.946 + // by screen readers. convert "tag" and "bookmark" to the localized versions, 1.947 + // but don't do anything for "favicon" (the default) 1.948 + if (aType != "favicon") { 1.949 + label += " " + this._bundle.GetStringFromName(aType + "ResultLabel"); 1.950 + } 1.951 + return label; 1.952 + ]]> 1.953 + </body> 1.954 + </method> 1.955 + 1.956 + </implementation> 1.957 + </binding> 1.958 + 1.959 + <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> 1.960 + <content align="start"> 1.961 + <xul:image class="popup-notification-icon" 1.962 + xbl:inherits="popupid,src=icon"/> 1.963 + <xul:vbox flex="1"> 1.964 + <xul:description class="popup-notification-description addon-progress-description" 1.965 + xbl:inherits="xbl:text=label"/> 1.966 + <xul:spacer flex="1"/> 1.967 + <xul:hbox align="center"> 1.968 + <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/> 1.969 + <xul:button anonid="cancel" class="popup-progress-cancel" oncommand="document.getBindingParent(this).cancel()"/> 1.970 + </xul:hbox> 1.971 + <xul:label anonid="progresstext" class="popup-progress-label"/> 1.972 + <xul:hbox class="popup-notification-button-container" 1.973 + pack="end" align="center"> 1.974 + <xul:button anonid="button" 1.975 + class="popup-notification-menubutton" 1.976 + type="menu-button" 1.977 + xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey"> 1.978 + <xul:menupopup anonid="menupopup" 1.979 + xbl:inherits="oncommand=menucommand"> 1.980 + <children/> 1.981 + <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon" 1.982 + label="&closeNotificationItem.label;" 1.983 + xbl:inherits="oncommand=closeitemcommand"/> 1.984 + </xul:menupopup> 1.985 + </xul:button> 1.986 + </xul:hbox> 1.987 + </xul:vbox> 1.988 + <xul:vbox pack="start"> 1.989 + <xul:toolbarbutton anonid="closebutton" 1.990 + class="messageCloseButton close-icon popup-notification-closebutton tabbable" 1.991 + xbl:inherits="oncommand=closebuttoncommand" 1.992 + tooltiptext="&closeNotification.tooltip;"/> 1.993 + </xul:vbox> 1.994 + </content> 1.995 + <implementation> 1.996 + <constructor><![CDATA[ 1.997 + this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip")); 1.998 + 1.999 + this.notification.options.installs.forEach(function(aInstall) { 1.1000 + aInstall.addListener(this); 1.1001 + }, this); 1.1002 + 1.1003 + // Calling updateProgress can sometimes cause this notification to be 1.1004 + // removed in the middle of refreshing the notification panel which 1.1005 + // makes the panel get refreshed again. Just initialise to the 1.1006 + // undetermined state and then schedule a proper check at the next 1.1007 + // opportunity 1.1008 + this.setProgress(0, -1); 1.1009 + this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0); 1.1010 + ]]></constructor> 1.1011 + 1.1012 + <destructor><![CDATA[ 1.1013 + this.destroy(); 1.1014 + ]]></destructor> 1.1015 + 1.1016 + <field name="progressmeter" readonly="true"> 1.1017 + document.getAnonymousElementByAttribute(this, "anonid", "progressmeter"); 1.1018 + </field> 1.1019 + <field name="progresstext" readonly="true"> 1.1020 + document.getAnonymousElementByAttribute(this, "anonid", "progresstext"); 1.1021 + </field> 1.1022 + <field name="cancelbtn" readonly="true"> 1.1023 + document.getAnonymousElementByAttribute(this, "anonid", "cancel"); 1.1024 + </field> 1.1025 + <field name="DownloadUtils" readonly="true"> 1.1026 + let utils = {}; 1.1027 + Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils); 1.1028 + utils.DownloadUtils; 1.1029 + </field> 1.1030 + 1.1031 + <method name="destroy"> 1.1032 + <body><![CDATA[ 1.1033 + this.notification.options.installs.forEach(function(aInstall) { 1.1034 + aInstall.removeListener(this); 1.1035 + }, this); 1.1036 + clearTimeout(this._updateProgressTimeout); 1.1037 + ]]></body> 1.1038 + </method> 1.1039 + 1.1040 + <method name="setProgress"> 1.1041 + <parameter name="aProgress"/> 1.1042 + <parameter name="aMaxProgress"/> 1.1043 + <body><![CDATA[ 1.1044 + if (aMaxProgress == -1) { 1.1045 + this.progressmeter.mode = "undetermined"; 1.1046 + } 1.1047 + else { 1.1048 + this.progressmeter.mode = "determined"; 1.1049 + this.progressmeter.value = (aProgress * 100) / aMaxProgress; 1.1050 + } 1.1051 + 1.1052 + let now = Date.now(); 1.1053 + 1.1054 + if (!this.notification.lastUpdate) { 1.1055 + this.notification.lastUpdate = now; 1.1056 + this.notification.lastProgress = aProgress; 1.1057 + return; 1.1058 + } 1.1059 + 1.1060 + let delta = now - this.notification.lastUpdate; 1.1061 + if ((delta < 400) && (aProgress < aMaxProgress)) 1.1062 + return; 1.1063 + 1.1064 + delta /= 1000; 1.1065 + 1.1066 + // This code is taken from nsDownloadManager.cpp 1.1067 + let speed = (aProgress - this.notification.lastProgress) / delta; 1.1068 + if (this.notification.speed) 1.1069 + speed = speed * 0.9 + this.notification.speed * 0.1; 1.1070 + 1.1071 + this.notification.lastUpdate = now; 1.1072 + this.notification.lastProgress = aProgress; 1.1073 + this.notification.speed = speed; 1.1074 + 1.1075 + let status = null; 1.1076 + [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last); 1.1077 + this.progresstext.value = status; 1.1078 + ]]></body> 1.1079 + </method> 1.1080 + 1.1081 + <method name="cancel"> 1.1082 + <body><![CDATA[ 1.1083 + // Cache these as cancelling the installs will remove this 1.1084 + // notification which will drop these references 1.1085 + let browser = this.notification.browser; 1.1086 + let contentWindow = this.notification.options.contentWindow; 1.1087 + let sourceURI = this.notification.options.sourceURI; 1.1088 + 1.1089 + let installs = this.notification.options.installs; 1.1090 + installs.forEach(function(aInstall) { 1.1091 + try { 1.1092 + aInstall.cancel(); 1.1093 + } 1.1094 + catch (e) { 1.1095 + // Cancel will throw if the download has already failed 1.1096 + } 1.1097 + }, this); 1.1098 + 1.1099 + let anchorID = "addons-notification-icon"; 1.1100 + let notificationID = "addon-install-cancelled"; 1.1101 + let messageString = gNavigatorBundle.getString("addonDownloadCancelled"); 1.1102 + messageString = PluralForm.get(installs.length, messageString); 1.1103 + let buttonText = gNavigatorBundle.getString("addonDownloadRestart"); 1.1104 + buttonText = PluralForm.get(installs.length, buttonText); 1.1105 + 1.1106 + let action = { 1.1107 + label: buttonText, 1.1108 + accessKey: gNavigatorBundle.getString("addonDownloadRestart.accessKey"), 1.1109 + callback: function() { 1.1110 + let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]. 1.1111 + getService(Ci.amIWebInstallListener); 1.1112 + if (weblistener.onWebInstallRequested(contentWindow, sourceURI, 1.1113 + installs, installs.length)) { 1.1114 + installs.forEach(function(aInstall) { 1.1115 + aInstall.install(); 1.1116 + }); 1.1117 + } 1.1118 + } 1.1119 + }; 1.1120 + 1.1121 + PopupNotifications.show(browser, notificationID, messageString, 1.1122 + anchorID, action); 1.1123 + ]]></body> 1.1124 + </method> 1.1125 + 1.1126 + <method name="updateProgress"> 1.1127 + <body><![CDATA[ 1.1128 + let downloadingCount = 0; 1.1129 + let progress = 0; 1.1130 + let maxProgress = 0; 1.1131 + 1.1132 + this.notification.options.installs.forEach(function(aInstall) { 1.1133 + if (aInstall.maxProgress == -1) 1.1134 + maxProgress = -1; 1.1135 + progress += aInstall.progress; 1.1136 + if (maxProgress >= 0) 1.1137 + maxProgress += aInstall.maxProgress; 1.1138 + if (aInstall.state < AddonManager.STATE_DOWNLOADED) 1.1139 + downloadingCount++; 1.1140 + }); 1.1141 + 1.1142 + if (downloadingCount == 0) { 1.1143 + this.destroy(); 1.1144 + PopupNotifications.remove(this.notification); 1.1145 + } 1.1146 + else { 1.1147 + this.setProgress(progress, maxProgress); 1.1148 + } 1.1149 + ]]></body> 1.1150 + </method> 1.1151 + 1.1152 + <method name="onDownloadProgress"> 1.1153 + <body><![CDATA[ 1.1154 + this.updateProgress(); 1.1155 + ]]></body> 1.1156 + </method> 1.1157 + 1.1158 + <method name="onDownloadFailed"> 1.1159 + <body><![CDATA[ 1.1160 + this.updateProgress(); 1.1161 + ]]></body> 1.1162 + </method> 1.1163 + 1.1164 + <method name="onDownloadCancelled"> 1.1165 + <body><![CDATA[ 1.1166 + this.updateProgress(); 1.1167 + ]]></body> 1.1168 + </method> 1.1169 + 1.1170 + <method name="onDownloadEnded"> 1.1171 + <body><![CDATA[ 1.1172 + this.updateProgress(); 1.1173 + ]]></body> 1.1174 + </method> 1.1175 + </implementation> 1.1176 + </binding> 1.1177 + 1.1178 + <binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> 1.1179 + <content align="start"> 1.1180 + 1.1181 + <xul:image class="popup-notification-icon" 1.1182 + xbl:inherits="popupid,src=icon"/> 1.1183 + 1.1184 + <xul:vbox flex="1"> 1.1185 + <xul:vbox anonid="identity-deck"> 1.1186 + <xul:vbox flex="1" pack="center"> <!-- 1: add an email --> 1.1187 + <html:input type="email" anonid="email" required="required" size="30"/> 1.1188 + <xul:description anonid="newidentitydesc"/> 1.1189 + <xul:spacer flex="1"/> 1.1190 + <xul:label class="text-link custom-link small-margin" anonid="chooseemail" hidden="true"/> 1.1191 + </xul:vbox> 1.1192 + <xul:vbox flex="1" hidden="true"> <!-- 2: choose an email --> 1.1193 + <xul:description anonid="chooseidentitydesc"/> 1.1194 + <xul:radiogroup anonid="identities"> 1.1195 + </xul:radiogroup> 1.1196 + <xul:label class="text-link custom-link" anonid="newemail"/> 1.1197 + </xul:vbox> 1.1198 + </xul:vbox> 1.1199 + <xul:hbox class="popup-notification-button-container" 1.1200 + pack="end" align="center"> 1.1201 + <xul:label anonid="tos" class="text-link" hidden="true"/> 1.1202 + <xul:label anonid="privacypolicy" class="text-link" hidden="true"/> 1.1203 + <xul:spacer flex="1"/> 1.1204 + <xul:image anonid="throbber" src="chrome://browser/skin/tabbrowser/loading.png" 1.1205 + style="visibility:hidden" width="16" height="16"/> 1.1206 + <xul:button anonid="button" 1.1207 + type="menu-button" 1.1208 + class="popup-notification-menubutton" 1.1209 + xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey"> 1.1210 + <xul:menupopup anonid="menupopup" 1.1211 + xbl:inherits="oncommand=menucommand"> 1.1212 + <children/> 1.1213 + <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon" 1.1214 + label="&closeNotificationItem.label;" 1.1215 + xbl:inherits="oncommand=closeitemcommand"/> 1.1216 + </xul:menupopup> 1.1217 + </xul:button> 1.1218 + </xul:hbox> 1.1219 + </xul:vbox> 1.1220 + <xul:vbox pack="start"> 1.1221 + <xul:toolbarbutton anonid="closebutton" 1.1222 + class="messageCloseButton close-icon popup-notification-closebutton tabbable" 1.1223 + xbl:inherits="oncommand=closebuttoncommand" 1.1224 + tooltiptext="&closeNotification.tooltip;"/> 1.1225 + </xul:vbox> 1.1226 + </content> 1.1227 + <implementation> 1.1228 + <constructor><![CDATA[ 1.1229 + // this.notification.options.identity is used to pass identity-specific info to the binding 1.1230 + let origin = this.identity.origin 1.1231 + 1.1232 + // Populate text 1.1233 + this.emailField.placeholder = gNavigatorBundle. 1.1234 + getString("identity.newIdentity.email.placeholder"); 1.1235 + this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString( 1.1236 + "identity.newIdentity.description", [origin]); 1.1237 + this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString( 1.1238 + "identity.chooseIdentity.description", [origin]); 1.1239 + 1.1240 + // Show optional terms of service and privacy policy links 1.1241 + this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService"); 1.1242 + this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy"); 1.1243 + 1.1244 + // Populate the list of identities to choose from. The origin is used to provide 1.1245 + // better suggestions. 1.1246 + let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin); 1.1247 + 1.1248 + this._populateIdentityList(identities); 1.1249 + 1.1250 + if (typeof this.step == "undefined") { 1.1251 + // First opening of this notification 1.1252 + // Show the add email pane (0) if there are no existing identities otherwise show the list 1.1253 + this.step = "result" in identities && identities.result.length ? 1 : 0; 1.1254 + } else { 1.1255 + // Already opened so restore previous state 1.1256 + if (this.identity.typedEmail) { 1.1257 + this.emailField.value = this.identity.typedEmail; 1.1258 + } 1.1259 + if (this.identity.selected) { 1.1260 + // If the user already chose an identity then update the UI to reflect that 1.1261 + this.onIdentitySelected(); 1.1262 + } 1.1263 + // Update the view for the step 1.1264 + this.step = this.step; 1.1265 + } 1.1266 + 1.1267 + // Fire notification with the chosen identity when main button is clicked 1.1268 + this.button.addEventListener("command", this._onButtonCommand.bind(this), true); 1.1269 + 1.1270 + // Do the same if enter is pressed in the email field 1.1271 + this.emailField.addEventListener("keypress", function emailFieldKeypress(aEvent) { 1.1272 + if (aEvent.keyCode != aEvent.DOM_VK_RETURN) 1.1273 + return; 1.1274 + this._onButtonCommand(aEvent); 1.1275 + }.bind(this)); 1.1276 + 1.1277 + this.addEmailLink.value = gNavigatorBundle.getString("identity.newIdentity.label"); 1.1278 + this.addEmailLink.accessKey = gNavigatorBundle.getString("identity.newIdentity.accessKey"); 1.1279 + this.addEmailLink.addEventListener("click", function addEmailClick(evt) { 1.1280 + this.step = 0; 1.1281 + }.bind(this)); 1.1282 + 1.1283 + this.chooseEmailLink.value = gNavigatorBundle.getString("identity.chooseIdentity.label"); 1.1284 + this.chooseEmailLink.hidden = !("result" in identities && identities.result.length); 1.1285 + this.chooseEmailLink.addEventListener("click", function chooseEmailClick(evt) { 1.1286 + this.step = 1; 1.1287 + }.bind(this)); 1.1288 + 1.1289 + this.emailField.addEventListener("blur", function onEmailBlur() { 1.1290 + this.identity.typedEmail = this.emailField.value; 1.1291 + }.bind(this)); 1.1292 + ]]></constructor> 1.1293 + 1.1294 + <field name="SignInToWebsiteUX" readonly="true"> 1.1295 + let sitw = {}; 1.1296 + Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); 1.1297 + sitw.SignInToWebsiteUX; 1.1298 + </field> 1.1299 + 1.1300 + <field name="newIdentityDesc" readonly="true"> 1.1301 + document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc"); 1.1302 + </field> 1.1303 + 1.1304 + <field name="chooseIdentityDesc" readonly="true"> 1.1305 + document.getAnonymousElementByAttribute(this, "anonid", "chooseidentitydesc"); 1.1306 + </field> 1.1307 + 1.1308 + <field name="identityList" readonly="true"> 1.1309 + document.getAnonymousElementByAttribute(this, "anonid", "identities"); 1.1310 + </field> 1.1311 + 1.1312 + <field name="emailField" readonly="true"> 1.1313 + document.getAnonymousElementByAttribute(this, "anonid", "email"); 1.1314 + </field> 1.1315 + 1.1316 + <field name="addEmailLink" readonly="true"> 1.1317 + document.getAnonymousElementByAttribute(this, "anonid", "newemail"); 1.1318 + </field> 1.1319 + 1.1320 + <field name="chooseEmailLink" readonly="true"> 1.1321 + document.getAnonymousElementByAttribute(this, "anonid", "chooseemail"); 1.1322 + </field> 1.1323 + 1.1324 + <field name="throbber" readonly="true"> 1.1325 + document.getAnonymousElementByAttribute(this, "anonid", "throbber"); 1.1326 + </field> 1.1327 + 1.1328 + <field name="identity" readonly="true"> 1.1329 + this.notification.options.identity; 1.1330 + </field> 1.1331 + 1.1332 + <!-- persist the state on the identity object so we can re-create the 1.1333 + notification state upon re-opening --> 1.1334 + <property name="step"> 1.1335 + <getter> 1.1336 + return this.identity.step; 1.1337 + </getter> 1.1338 + <setter><![CDATA[ 1.1339 + let deck = document.getAnonymousElementByAttribute(this, "anonid", "identity-deck"); 1.1340 + for (let i = 0; i < deck.children.length; i++) { 1.1341 + deck.children[i].hidden = (val != i); 1.1342 + } 1.1343 + this.identity.step = val; 1.1344 + switch (val) { 1.1345 + case 0: 1.1346 + this.emailField.focus(); 1.1347 + break; 1.1348 + }]]> 1.1349 + </setter> 1.1350 + </property> 1.1351 + 1.1352 + <method name="onIdentitySelected"> 1.1353 + <body><![CDATA[ 1.1354 + this.throbber.style.visibility = "visible"; 1.1355 + this.button.disabled = true; 1.1356 + this.emailField.value = this.identity.selected 1.1357 + this.emailField.disabled = true; 1.1358 + this.identityList.disabled = true; 1.1359 + ]]></body> 1.1360 + </method> 1.1361 + 1.1362 + <method name="_populateLink"> 1.1363 + <parameter name="aURL"/> 1.1364 + <parameter name="aLinkId"/> 1.1365 + <parameter name="aStringId"/> 1.1366 + <body><![CDATA[ 1.1367 + if (aURL) { 1.1368 + // Show optional link to aURL 1.1369 + let link = document.getAnonymousElementByAttribute(this, "anonid", aLinkId); 1.1370 + link.value = gNavigatorBundle.getString(aStringId); 1.1371 + link.href = aURL; 1.1372 + link.hidden = false; 1.1373 + } 1.1374 + ]]></body> 1.1375 + </method> 1.1376 + 1.1377 + <method name="_populateIdentityList"> 1.1378 + <parameter name="aIdentities"/> 1.1379 + <body><![CDATA[ 1.1380 + let foundLastUsed = false; 1.1381 + let lastUsed = this.identity.selected || aIdentities.lastUsed; 1.1382 + for (let id in aIdentities.result) { 1.1383 + let label = aIdentities.result[id]; 1.1384 + let opt = this.identityList.appendItem(label); 1.1385 + if (label == lastUsed) { 1.1386 + this.identityList.selectedItem = opt; 1.1387 + foundLastUsed = true; 1.1388 + } 1.1389 + } 1.1390 + if (!foundLastUsed) { 1.1391 + this.identityList.selectedIndex = -1; 1.1392 + } 1.1393 + ]]></body> 1.1394 + </method> 1.1395 + 1.1396 + <method name="_onButtonCommand"> 1.1397 + <parameter name="aEvent"/> 1.1398 + <body><![CDATA[ 1.1399 + if (aEvent.target != aEvent.currentTarget) 1.1400 + return; 1.1401 + let chosenId; 1.1402 + switch (this.step) { 1.1403 + case 0: 1.1404 + aEvent.stopPropagation(); 1.1405 + if (!this.emailField.validity.valid) { 1.1406 + this.emailField.focus(); 1.1407 + return; 1.1408 + } 1.1409 + chosenId = this.emailField.value; 1.1410 + break; 1.1411 + case 1: 1.1412 + aEvent.stopPropagation(); 1.1413 + let selectedItem = this.identityList.selectedItem 1.1414 + chosenId = selectedItem ? selectedItem.label : null; 1.1415 + if (!chosenId) 1.1416 + return; 1.1417 + break; 1.1418 + default: 1.1419 + throw new Error("Unknown case"); 1.1420 + return; 1.1421 + } 1.1422 + // Actually select the identity 1.1423 + this.SignInToWebsiteUX.selectIdentity(this.identity.rpId, chosenId); 1.1424 + this.identity.selected = chosenId; 1.1425 + this.onIdentitySelected(); 1.1426 + ]]></body> 1.1427 + </method> 1.1428 + 1.1429 + </implementation> 1.1430 + </binding> 1.1431 + 1.1432 + <binding id="plugin-popupnotification-center-item"> 1.1433 + <content align="center"> 1.1434 + <xul:vbox pack="center" anonid="itemBox" class="itemBox"> 1.1435 + <xul:description anonid="center-item-label" class="center-item-label" /> 1.1436 + <xul:hbox flex="1" pack="start" align="center" anonid="center-item-warning"> 1.1437 + <xul:image anonid="center-item-warning-icon" class="center-item-warning-icon"/> 1.1438 + <xul:label anonid="center-item-warning-label"/> 1.1439 + <xul:label anonid="center-item-link" value="&checkForUpdates;" class="text-link"/> 1.1440 + </xul:hbox> 1.1441 + </xul:vbox> 1.1442 + <xul:vbox pack="center"> 1.1443 + <xul:menulist class="center-item-menulist" 1.1444 + anonid="center-item-menulist"> 1.1445 + <xul:menupopup> 1.1446 + <xul:menuitem anonid="allownow" value="allownow" 1.1447 + label="&pluginActivateNow.label;" /> 1.1448 + <xul:menuitem anonid="allowalways" value="allowalways" 1.1449 + label="&pluginActivateAlways.label;" /> 1.1450 + <xul:menuitem anonid="block" value="block" 1.1451 + label="&pluginBlockNow.label;" /> 1.1452 + </xul:menupopup> 1.1453 + </xul:menulist> 1.1454 + </xul:vbox> 1.1455 + </content> 1.1456 + <resources> 1.1457 + <stylesheet src="chrome://global/skin/notification.css"/> 1.1458 + </resources> 1.1459 + <implementation> 1.1460 + <constructor><![CDATA[ 1.1461 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-label").value = this.action.pluginName; 1.1462 + 1.1463 + let curState = "block"; 1.1464 + if (this.action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { 1.1465 + if (this.action.pluginPermissionType == Ci.nsIPermissionManager.EXPIRE_SESSION) { 1.1466 + curState = "allownow"; 1.1467 + } 1.1468 + else { 1.1469 + curState = "allowalways"; 1.1470 + } 1.1471 + } 1.1472 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").value = curState; 1.1473 + 1.1474 + let warningString = ""; 1.1475 + let linkString = ""; 1.1476 + 1.1477 + let link = document.getAnonymousElementByAttribute(this, "anonid", "center-item-link"); 1.1478 + 1.1479 + let url; 1.1480 + let linkHandler; 1.1481 + 1.1482 + if (this.action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { 1.1483 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; 1.1484 + warningString = gNavigatorBundle.getString("pluginActivateDisabled.label"); 1.1485 + linkString = gNavigatorBundle.getString("pluginActivateDisabled.manage"); 1.1486 + linkHandler = function(event) { 1.1487 + event.preventDefault(); 1.1488 + gPluginHandler.managePlugins(); 1.1489 + }; 1.1490 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-icon").hidden = true; 1.1491 + } 1.1492 + else { 1.1493 + url = this.action.detailsLink; 1.1494 + 1.1495 + switch (this.action.blocklistState) { 1.1496 + case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: 1.1497 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning").hidden = true; 1.1498 + break; 1.1499 + case Ci.nsIBlocklistService.STATE_BLOCKED: 1.1500 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; 1.1501 + warningString = gNavigatorBundle.getString("pluginActivateBlocked.label"); 1.1502 + linkString = gNavigatorBundle.getString("pluginActivate.learnMore"); 1.1503 + break; 1.1504 + case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: 1.1505 + warningString = gNavigatorBundle.getString("pluginActivateOutdated.label"); 1.1506 + linkString = gNavigatorBundle.getString("pluginActivate.updateLabel"); 1.1507 + break; 1.1508 + case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: 1.1509 + warningString = gNavigatorBundle.getString("pluginActivateVulnerable.label"); 1.1510 + linkString = gNavigatorBundle.getString("pluginActivate.riskLabel"); 1.1511 + break; 1.1512 + } 1.1513 + } 1.1514 + document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString; 1.1515 + 1.1516 + if (url || linkHandler) { 1.1517 + link.value = linkString; 1.1518 + if (url) { 1.1519 + link.href = url; 1.1520 + } 1.1521 + if (linkHandler) { 1.1522 + link.addEventListener("click", linkHandler, false); 1.1523 + } 1.1524 + } 1.1525 + else { 1.1526 + link.hidden = true; 1.1527 + } 1.1528 + ]]></constructor> 1.1529 + <property name="value"> 1.1530 + <getter> 1.1531 + return document.getAnonymousElementByAttribute(this, "anonid", 1.1532 + "center-item-menulist").value; 1.1533 + </getter> 1.1534 + <setter><!-- This should be used only in automated tests --> 1.1535 + document.getAnonymousElementByAttribute(this, "anonid", 1.1536 + "center-item-menulist").value = val; 1.1537 + </setter> 1.1538 + </property> 1.1539 + </implementation> 1.1540 + </binding> 1.1541 + 1.1542 + <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> 1.1543 + <content align="start" style="width: &pluginNotification.width;;"> 1.1544 + <xul:vbox flex="1" align="stretch" class="popup-notification-main-box" 1.1545 + xbl:inherits="popupid"> 1.1546 + <xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start"> 1.1547 + <xul:description class="click-to-play-plugins-outer-description" flex="1"> 1.1548 + <html:span anonid="click-to-play-plugins-notification-description" /> 1.1549 + <xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" /> 1.1550 + </xul:description> 1.1551 + <xul:toolbarbutton anonid="closebutton" 1.1552 + class="messageCloseButton popup-notification-closebutton tabbable close-icon" 1.1553 + xbl:inherits="oncommand=closebuttoncommand" 1.1554 + tooltiptext="&closeNotification.tooltip;"/> 1.1555 + </xul:hbox> 1.1556 + <xul:grid anonid="click-to-play-plugins-notification-center-box" 1.1557 + class="click-to-play-plugins-notification-center-box"> 1.1558 + <xul:columns> 1.1559 + <xul:column flex="1"/> 1.1560 + <xul:column/> 1.1561 + </xul:columns> 1.1562 + <xul:rows> 1.1563 + <children includes="row"/> 1.1564 + <xul:hbox pack="start" anonid="plugin-notification-showbox"> 1.1565 + <xul:button label="&pluginNotification.showAll.label;" 1.1566 + accesskey="&pluginNotification.showAll.accesskey;" 1.1567 + class="plugin-notification-showbutton" 1.1568 + oncommand="document.getBindingParent(this)._setState(2)"/> 1.1569 + </xul:hbox> 1.1570 + </xul:rows> 1.1571 + </xul:grid> 1.1572 + <xul:hbox anonid="button-container" 1.1573 + class="click-to-play-plugins-notification-button-container" 1.1574 + pack="center" align="center"> 1.1575 + <xul:button anonid="primarybutton" 1.1576 + class="click-to-play-popup-button" 1.1577 + oncommand="document.getBindingParent(this)._onButton(this)" 1.1578 + flex="1"/> 1.1579 + <xul:button anonid="secondarybutton" 1.1580 + class="click-to-play-popup-button" 1.1581 + oncommand="document.getBindingParent(this)._onButton(this);" 1.1582 + flex="1"/> 1.1583 + </xul:hbox> 1.1584 + <xul:box hidden="true"> 1.1585 + <children/> 1.1586 + </xul:box> 1.1587 + </xul:vbox> 1.1588 + </content> 1.1589 + <resources> 1.1590 + <stylesheet src="chrome://global/skin/notification.css"/> 1.1591 + </resources> 1.1592 + <implementation> 1.1593 + <field name="_states"> 1.1594 + ({SINGLE: 0, MULTI_COLLAPSED: 1, MULTI_EXPANDED: 2}) 1.1595 + </field> 1.1596 + <field name="_primaryButton"> 1.1597 + document.getAnonymousElementByAttribute(this, "anonid", "primarybutton"); 1.1598 + </field> 1.1599 + <field name="_secondaryButton"> 1.1600 + document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton") 1.1601 + </field> 1.1602 + <field name="_buttonContainer"> 1.1603 + document.getAnonymousElementByAttribute(this, "anonid", "button-container") 1.1604 + </field> 1.1605 + <field name="_brandShortName"> 1.1606 + document.getElementById("bundle_brand").getString("brandShortName") 1.1607 + </field> 1.1608 + <field name="_items">[]</field> 1.1609 + <constructor><![CDATA[ 1.1610 + const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.1611 + let sortedActions = []; 1.1612 + for (let action of this.notification.options.pluginData.values()) { 1.1613 + sortedActions.push(action); 1.1614 + } 1.1615 + sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName)); 1.1616 + 1.1617 + for (let action of sortedActions) { 1.1618 + let item = document.createElementNS(XUL_NS, "row"); 1.1619 + item.setAttribute("class", "plugin-popupnotification-centeritem"); 1.1620 + item.action = action; 1.1621 + this.appendChild(item); 1.1622 + this._items.push(item); 1.1623 + } 1.1624 + switch (this._items.length) { 1.1625 + case 0: 1.1626 + PopupNotifications._dismiss(); 1.1627 + break; 1.1628 + case 1: 1.1629 + this._setState(this._states.SINGLE); 1.1630 + break; 1.1631 + default: 1.1632 + if (this.notification.options.primaryPlugin) { 1.1633 + this._setState(this._states.MULTI_COLLAPSED); 1.1634 + } else { 1.1635 + this._setState(this._states.MULTI_EXPANDED); 1.1636 + } 1.1637 + } 1.1638 + ]]></constructor> 1.1639 + <method name="_setState"> 1.1640 + <parameter name="state" /> 1.1641 + <body><![CDATA[ 1.1642 + var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box"); 1.1643 + 1.1644 + if (this._states.SINGLE == state) { 1.1645 + grid.hidden = true; 1.1646 + this._setupSingleState(); 1.1647 + return; 1.1648 + } 1.1649 + 1.1650 + let host = gPluginHandler._getHostFromPrincipal(this.notification.browser.contentWindow.document.nodePrincipal); 1.1651 + this._setupDescription("pluginActivateMultiple.message", null, host); 1.1652 + 1.1653 + var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox"); 1.1654 + 1.1655 + var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties"); 1.1656 + this._primaryButton.label = dialogStrings.GetStringFromName("button-accept"); 1.1657 + this._primaryButton.setAttribute("default", "true"); 1.1658 + 1.1659 + this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel"); 1.1660 + this._primaryButton.setAttribute("action", "_multiAccept"); 1.1661 + this._secondaryButton.setAttribute("action", "_cancel"); 1.1662 + 1.1663 + grid.hidden = false; 1.1664 + 1.1665 + if (this._states.MULTI_COLLAPSED == state) { 1.1666 + for (let child of this.childNodes) { 1.1667 + if (child.tagName != "row") { 1.1668 + continue; 1.1669 + } 1.1670 + child.hidden = this.notification.options.primaryPlugin != 1.1671 + child.action.permissionString; 1.1672 + } 1.1673 + showBox.hidden = false; 1.1674 + } 1.1675 + else { 1.1676 + for (let child of this.childNodes) { 1.1677 + if (child.tagName != "row") { 1.1678 + continue; 1.1679 + } 1.1680 + child.hidden = false; 1.1681 + } 1.1682 + showBox.hidden = true; 1.1683 + } 1.1684 + this._setupLink(null); 1.1685 + ]]></body> 1.1686 + </method> 1.1687 + <method name="_setupSingleState"> 1.1688 + <body><![CDATA[ 1.1689 + var action = this._items[0].action; 1.1690 + var host = action.pluginPermissionHost; 1.1691 + 1.1692 + let label, linkLabel, linkUrl, button1, button2; 1.1693 + 1.1694 + if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { 1.1695 + button1 = { 1.1696 + label: "pluginBlockNow.label", 1.1697 + accesskey: "pluginBlockNow.accesskey", 1.1698 + action: "_singleBlock" 1.1699 + }; 1.1700 + button2 = { 1.1701 + label: "pluginContinue.label", 1.1702 + accesskey: "pluginContinue.accesskey", 1.1703 + action: "_singleContinue", 1.1704 + default: true 1.1705 + }; 1.1706 + switch (action.blocklistState) { 1.1707 + case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: 1.1708 + label = "pluginEnabled.message"; 1.1709 + linkLabel = "pluginActivate.learnMore"; 1.1710 + break; 1.1711 + 1.1712 + case Ci.nsIBlocklistService.STATE_BLOCKED: 1.1713 + Cu.reportError(Error("Cannot happen!")); 1.1714 + break; 1.1715 + 1.1716 + case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: 1.1717 + label = "pluginEnabledOutdated.message"; 1.1718 + linkLabel = "pluginActivate.updateLabel"; 1.1719 + break; 1.1720 + 1.1721 + case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: 1.1722 + label = "pluginEnabledVulnerable.message"; 1.1723 + linkLabel = "pluginActivate.riskLabel" 1.1724 + break; 1.1725 + 1.1726 + default: 1.1727 + Cu.reportError(Error("Unexpected blocklist state")); 1.1728 + } 1.1729 + } 1.1730 + else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { 1.1731 + let linkElement = 1.1732 + document.getAnonymousElementByAttribute( 1.1733 + this, "anonid", "click-to-play-plugins-notification-link"); 1.1734 + linkElement.textContent = gNavigatorBundle.getString("pluginActivateDisabled.manage"); 1.1735 + linkElement.setAttribute("onclick", "gPluginHandler.managePlugins()"); 1.1736 + 1.1737 + let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); 1.1738 + descElement.textContent = gNavigatorBundle.getFormattedString( 1.1739 + "pluginActivateDisabled.message", [action.pluginName, this._brandShortName]) + " "; 1.1740 + this._buttonContainer.hidden = true; 1.1741 + return; 1.1742 + } 1.1743 + else if (action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { 1.1744 + let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); 1.1745 + descElement.textContent = gNavigatorBundle.getFormattedString( 1.1746 + "pluginActivateBlocked.message", [action.pluginName, this._brandShortName]) + " "; 1.1747 + this._setupLink("pluginActivate.learnMore", action.detailsLink); 1.1748 + this._buttonContainer.hidden = true; 1.1749 + return; 1.1750 + } 1.1751 + else { 1.1752 + button1 = { 1.1753 + label: "pluginActivateNow.label", 1.1754 + accesskey: "pluginActivateNow.accesskey", 1.1755 + action: "_singleActivateNow" 1.1756 + }; 1.1757 + button2 = { 1.1758 + label: "pluginActivateAlways.label", 1.1759 + accesskey: "pluginActivateAlways.accesskey", 1.1760 + action: "_singleActivateAlways" 1.1761 + }; 1.1762 + switch (action.blocklistState) { 1.1763 + case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: 1.1764 + label = "pluginActivateNew.message"; 1.1765 + linkLabel = "pluginActivate.learnMore"; 1.1766 + button2.default = true; 1.1767 + break; 1.1768 + 1.1769 + case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: 1.1770 + label = "pluginActivateOutdated.message"; 1.1771 + linkLabel = "pluginActivate.updateLabel"; 1.1772 + button1.default = true; 1.1773 + break; 1.1774 + 1.1775 + case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: 1.1776 + label = "pluginActivateVulnerable.message"; 1.1777 + linkLabel = "pluginActivate.riskLabel" 1.1778 + button1.default = true; 1.1779 + break; 1.1780 + 1.1781 + default: 1.1782 + Cu.reportError(Error("Unexpected blocklist state")); 1.1783 + } 1.1784 + } 1.1785 + this._setupDescription(label, action.pluginName, host); 1.1786 + this._setupLink(linkLabel, action.detailsLink); 1.1787 + 1.1788 + this._primaryButton.label = gNavigatorBundle.getString(button1.label); 1.1789 + this._primaryButton.accesskey = gNavigatorBundle.getString(button1.accesskey); 1.1790 + this._primaryButton.setAttribute("action", button1.action); 1.1791 + 1.1792 + this._secondaryButton.label = gNavigatorBundle.getString(button2.label); 1.1793 + this._secondaryButton.accesskey = gNavigatorBundle.getString(button2.accesskey); 1.1794 + this._secondaryButton.setAttribute("action", button2.action); 1.1795 + if (button1.default) { 1.1796 + this._primaryButton.setAttribute("default", "true"); 1.1797 + } 1.1798 + else if (button2.default) { 1.1799 + this._secondaryButton.setAttribute("default", "true"); 1.1800 + } 1.1801 + ]]></body> 1.1802 + </method> 1.1803 + <method name="_setupDescription"> 1.1804 + <parameter name="baseString" /> 1.1805 + <parameter name="pluginName" /> <!-- null for the multiple-plugin case --> 1.1806 + <parameter name="host" /> 1.1807 + <body><![CDATA[ 1.1808 + var bsn = this._brandShortName; 1.1809 + var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); 1.1810 + while (span.lastChild) { 1.1811 + span.removeChild(span.lastChild); 1.1812 + } 1.1813 + 1.1814 + var args = ["__host__", this._brandShortName]; 1.1815 + if (pluginName) { 1.1816 + args.unshift(pluginName); 1.1817 + } 1.1818 + var bases = gNavigatorBundle.getFormattedString(baseString, args). 1.1819 + split("__host__", 2); 1.1820 + 1.1821 + span.appendChild(document.createTextNode(bases[0])); 1.1822 + var hostSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em"); 1.1823 + hostSpan.appendChild(document.createTextNode(host)); 1.1824 + span.appendChild(hostSpan); 1.1825 + span.appendChild(document.createTextNode(bases[1] + " ")); 1.1826 + ]]></body> 1.1827 + </method> 1.1828 + <method name="_setupLink"> 1.1829 + <parameter name="linkString"/> 1.1830 + <parameter name="linkUrl" /> 1.1831 + <body><![CDATA[ 1.1832 + var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link"); 1.1833 + if (!linkString || !linkUrl) { 1.1834 + link.hidden = true; 1.1835 + return; 1.1836 + } 1.1837 + 1.1838 + link.hidden = false; 1.1839 + link.textContent = gNavigatorBundle.getString(linkString); 1.1840 + link.href = linkUrl; 1.1841 + ]]></body> 1.1842 + </method> 1.1843 + <method name="_onButton"> 1.1844 + <parameter name="aButton" /> 1.1845 + <body><![CDATA[ 1.1846 + let methodName = aButton.getAttribute("action"); 1.1847 + this[methodName](); 1.1848 + ]]></body> 1.1849 + </method> 1.1850 + <method name="_singleActivateNow"> 1.1851 + <body><![CDATA[ 1.1852 + gPluginHandler._updatePluginPermission(this.notification, 1.1853 + this._items[0].action, 1.1854 + "allownow"); 1.1855 + this._cancel(); 1.1856 + ]]></body> 1.1857 + </method> 1.1858 + <method name="_singleBlock"> 1.1859 + <body><![CDATA[ 1.1860 + gPluginHandler._updatePluginPermission(this.notification, 1.1861 + this._items[0].action, 1.1862 + "block"); 1.1863 + this._cancel(); 1.1864 + ]]></body> 1.1865 + </method> 1.1866 + <method name="_singleActivateAlways"> 1.1867 + <body><![CDATA[ 1.1868 + gPluginHandler._updatePluginPermission(this.notification, 1.1869 + this._items[0].action, 1.1870 + "allowalways"); 1.1871 + this._cancel(); 1.1872 + ]]></body> 1.1873 + </method> 1.1874 + <method name="_singleContinue"> 1.1875 + <body><![CDATA[ 1.1876 + gPluginHandler._updatePluginPermission(this.notification, 1.1877 + this._items[0].action, 1.1878 + "continue"); 1.1879 + this._cancel(); 1.1880 + ]]></body> 1.1881 + </method> 1.1882 + <method name="_multiAccept"> 1.1883 + <body><![CDATA[ 1.1884 + for (let item of this._items) { 1.1885 + let action = item.action; 1.1886 + if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED || 1.1887 + action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { 1.1888 + continue; 1.1889 + } 1.1890 + gPluginHandler._updatePluginPermission(this.notification, 1.1891 + item.action, item.value); 1.1892 + } 1.1893 + this._cancel(); 1.1894 + ]]></body> 1.1895 + </method> 1.1896 + <method name="_cancel"> 1.1897 + <body><![CDATA[ 1.1898 + PopupNotifications._dismiss(); 1.1899 + ]]></body> 1.1900 + </method> 1.1901 + <method name="_accept"> 1.1902 + <parameter name="aEvent" /> 1.1903 + <body><![CDATA[ 1.1904 + if (aEvent.defaultPrevented) 1.1905 + return; 1.1906 + aEvent.preventDefault(); 1.1907 + if (this._primaryButton.getAttribute("default") == "true") { 1.1908 + this._primaryButton.click(); 1.1909 + } 1.1910 + else if (this._secondaryButton.getAttribute("default") == "true") { 1.1911 + this._secondaryButton.click(); 1.1912 + } 1.1913 + ]]></body> 1.1914 + </method> 1.1915 + </implementation> 1.1916 + <handlers> 1.1917 + <!-- The _accept method checks for .defaultPrevented so that if focus is in a button, 1.1918 + enter activates the button and not this default action --> 1.1919 + <handler event="keypress" keycode="VK_RETURN" group="system" action="this._accept(event);"/> 1.1920 + </handlers> 1.1921 + </binding> 1.1922 + 1.1923 + <binding id="splitmenu"> 1.1924 + <content> 1.1925 + <xul:hbox anonid="menuitem" flex="1" 1.1926 + class="splitmenu-menuitem" 1.1927 + xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/> 1.1928 + <xul:menu anonid="menu" class="splitmenu-menu" 1.1929 + xbl:inherits="disabled,_moz-menuactive=active" 1.1930 + oncommand="event.stopPropagation();"> 1.1931 + <children includes="menupopup"/> 1.1932 + </xul:menu> 1.1933 + </content> 1.1934 + 1.1935 + <implementation implements="nsIDOMEventListener"> 1.1936 + <constructor><![CDATA[ 1.1937 + this._parentMenupopup.addEventListener("DOMMenuItemActive", this, false); 1.1938 + this._parentMenupopup.addEventListener("popuphidden", this, false); 1.1939 + ]]></constructor> 1.1940 + 1.1941 + <destructor><![CDATA[ 1.1942 + this._parentMenupopup.removeEventListener("DOMMenuItemActive", this, false); 1.1943 + this._parentMenupopup.removeEventListener("popuphidden", this, false); 1.1944 + ]]></destructor> 1.1945 + 1.1946 + <field name="menuitem" readonly="true"> 1.1947 + document.getAnonymousElementByAttribute(this, "anonid", "menuitem"); 1.1948 + </field> 1.1949 + <field name="menu" readonly="true"> 1.1950 + document.getAnonymousElementByAttribute(this, "anonid", "menu"); 1.1951 + </field> 1.1952 + 1.1953 + <field name="_menuDelay">600</field> 1.1954 + 1.1955 + <field name="_parentMenupopup"><![CDATA[ 1.1956 + this._getParentMenupopup(this); 1.1957 + ]]></field> 1.1958 + 1.1959 + <method name="_getParentMenupopup"> 1.1960 + <parameter name="aNode"/> 1.1961 + <body><![CDATA[ 1.1962 + let node = aNode.parentNode; 1.1963 + while (node) { 1.1964 + if (node.localName == "menupopup") 1.1965 + break; 1.1966 + node = node.parentNode; 1.1967 + } 1.1968 + return node; 1.1969 + ]]></body> 1.1970 + </method> 1.1971 + 1.1972 + <method name="handleEvent"> 1.1973 + <parameter name="event"/> 1.1974 + <body><![CDATA[ 1.1975 + switch (event.type) { 1.1976 + case "DOMMenuItemActive": 1.1977 + if (this.getAttribute("active") == "true" && 1.1978 + event.target != this && 1.1979 + this._getParentMenupopup(event.target) == this._parentMenupopup) 1.1980 + this.removeAttribute("active"); 1.1981 + break; 1.1982 + case "popuphidden": 1.1983 + if (event.target == this._parentMenupopup) 1.1984 + this.removeAttribute("active"); 1.1985 + break; 1.1986 + } 1.1987 + ]]></body> 1.1988 + </method> 1.1989 + </implementation> 1.1990 + 1.1991 + <handlers> 1.1992 + <handler event="mouseover"><![CDATA[ 1.1993 + if (this.getAttribute("active") != "true") { 1.1994 + this.setAttribute("active", "true"); 1.1995 + 1.1996 + let event = document.createEvent("Events"); 1.1997 + event.initEvent("DOMMenuItemActive", true, false); 1.1998 + this.dispatchEvent(event); 1.1999 + 1.2000 + if (this.getAttribute("disabled") != "true") { 1.2001 + let self = this; 1.2002 + setTimeout(function () { 1.2003 + if (self.getAttribute("active") == "true") 1.2004 + self.menu.open = true; 1.2005 + }, this._menuDelay); 1.2006 + } 1.2007 + } 1.2008 + ]]></handler> 1.2009 + 1.2010 + <handler event="popupshowing"><![CDATA[ 1.2011 + if (event.target == this.firstChild && 1.2012 + this._parentMenupopup._currentPopup) 1.2013 + this._parentMenupopup._currentPopup.hidePopup(); 1.2014 + ]]></handler> 1.2015 + 1.2016 + <handler event="click" phase="capturing"><![CDATA[ 1.2017 + if (this.getAttribute("disabled") == "true") { 1.2018 + // Prevent the command from being carried out 1.2019 + event.stopPropagation(); 1.2020 + return; 1.2021 + } 1.2022 + 1.2023 + let node = event.originalTarget; 1.2024 + while (true) { 1.2025 + if (node == this.menuitem) 1.2026 + break; 1.2027 + if (node == this) 1.2028 + return; 1.2029 + node = node.parentNode; 1.2030 + } 1.2031 + 1.2032 + this._parentMenupopup.hidePopup(); 1.2033 + ]]></handler> 1.2034 + </handlers> 1.2035 + </binding> 1.2036 + 1.2037 + <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem"> 1.2038 + <implementation> 1.2039 + <constructor><![CDATA[ 1.2040 + this.setAttribute("tooltiptext", this.getAttribute("acceltext")); 1.2041 + // TODO: Simplify this to this.setAttribute("acceltext", "") once bug 1.2042 + // 592424 is fixed 1.2043 + document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); 1.2044 + ]]></constructor> 1.2045 + </implementation> 1.2046 + </binding> 1.2047 + 1.2048 + <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic"> 1.2049 + <implementation> 1.2050 + <constructor><![CDATA[ 1.2051 + this.setAttribute("tooltiptext", this.getAttribute("acceltext")); 1.2052 + // TODO: Simplify this to this.setAttribute("acceltext", "") once bug 1.2053 + // 592424 is fixed 1.2054 + document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); 1.2055 + ]]></constructor> 1.2056 + </implementation> 1.2057 + </binding> 1.2058 + 1.2059 + <binding id="promobox"> 1.2060 + <content> 1.2061 + <xul:hbox class="panel-promo-box" align="start" flex="1"> 1.2062 + <xul:hbox align="center" flex="1"> 1.2063 + <xul:image class="panel-promo-icon"/> 1.2064 + <xul:description anonid="promo-message" class="panel-promo-message" flex="1"> 1.2065 + <xul:description anonid="promo-link" 1.2066 + class="plain text-link inline-link" 1.2067 + onclick="document.getBindingParent(this).onLinkClick();"/> 1.2068 + </xul:description> 1.2069 + </xul:hbox> 1.2070 + <xul:toolbarbutton class="panel-promo-closebutton close-icon" 1.2071 + oncommand="document.getBindingParent(this).onCloseButtonCommand();" 1.2072 + tooltiptext="&closeNotification.tooltip;"/> 1.2073 + </xul:hbox> 1.2074 + </content> 1.2075 + 1.2076 + <implementation implements="nsIDOMEventListener"> 1.2077 + <constructor><![CDATA[ 1.2078 + this._panel.addEventListener("popupshowing", this, false); 1.2079 + ]]></constructor> 1.2080 + 1.2081 + <destructor><![CDATA[ 1.2082 + this._panel.removeEventListener("popupshowing", this, false); 1.2083 + ]]></destructor> 1.2084 + 1.2085 + <field name="_panel" readonly="true"><![CDATA[ 1.2086 + let node = this.parentNode; 1.2087 + while(node && node.localName != "panel") { 1.2088 + node = node.parentNode; 1.2089 + } 1.2090 + node; 1.2091 + ]]></field> 1.2092 + <field name="_promomessage" readonly="true"> 1.2093 + document.getAnonymousElementByAttribute(this, "anonid", "promo-message"); 1.2094 + </field> 1.2095 + <field name="_promolink" readonly="true"> 1.2096 + document.getAnonymousElementByAttribute(this, "anonid", "promo-link"); 1.2097 + </field> 1.2098 + <field name="_brandBundle" readonly="true"> 1.2099 + Services.strings.createBundle("chrome://branding/locale/brand.properties"); 1.2100 + </field> 1.2101 + <property name="_viewsLeftMap"> 1.2102 + <getter><![CDATA[ 1.2103 + try { 1.2104 + return JSON.parse(Services.prefs.getCharPref("browser.syncPromoViewsLeftMap")); 1.2105 + } catch (ex) {} 1.2106 + return {}; 1.2107 + ]]></getter> 1.2108 + </property> 1.2109 + <property name="_viewsLeft"> 1.2110 + <getter><![CDATA[ 1.2111 + let views = 5; 1.2112 + let map = this._viewsLeftMap; 1.2113 + if (this._notificationType in map) { 1.2114 + views = map[this._notificationType]; 1.2115 + } 1.2116 + return views; 1.2117 + ]]></getter> 1.2118 + <setter><![CDATA[ 1.2119 + let map = this._viewsLeftMap; 1.2120 + map[this._notificationType] = val; 1.2121 + Services.prefs.setCharPref("browser.syncPromoViewsLeftMap", 1.2122 + JSON.stringify(map)); 1.2123 + return val; 1.2124 + ]]></setter> 1.2125 + </property> 1.2126 + <property name="_notificationType"> 1.2127 + <getter><![CDATA[ 1.2128 + // Use the popupid attribute to identify the notification type, 1.2129 + // otherwise just rely on the panel id for common arrowpanels. 1.2130 + let type = this._panel.firstChild.getAttribute("popupid") || 1.2131 + this._panel.id; 1.2132 + if (type.startsWith("password-")) 1.2133 + return "passwords"; 1.2134 + if (type == "editBookmarkPanel") 1.2135 + return "bookmarks"; 1.2136 + if (type == "addon-install-complete") { 1.2137 + if (!Services.prefs.prefHasUserValue("services.sync.username")) 1.2138 + return "addons"; 1.2139 + if (!Services.prefs.getBoolPref("services.sync.engine.addons")) 1.2140 + return "addons-sync-disabled"; 1.2141 + } 1.2142 + return null; 1.2143 + ]]></getter> 1.2144 + </property> 1.2145 + <property name="_notificationMessage"> 1.2146 + <getter><![CDATA[ 1.2147 + return gNavigatorBundle.getFormattedString( 1.2148 + "syncPromoNotification." + this._notificationType + ".description", 1.2149 + [this._brandBundle.GetStringFromName("syncBrandShortName")] 1.2150 + ); 1.2151 + ]]></getter> 1.2152 + </property> 1.2153 + <property name="_notificationLink"> 1.2154 + <getter><![CDATA[ 1.2155 + if (this._notificationType == "addons-sync-disabled") { 1.2156 + return "https://support.mozilla.org/kb/how-do-i-enable-add-sync"; 1.2157 + } 1.2158 + return "https://services.mozilla.com/sync/"; 1.2159 + ]]></getter> 1.2160 + </property> 1.2161 + <method name="onCloseButtonCommand"> 1.2162 + <body><![CDATA[ 1.2163 + this._viewsLeft = 0; 1.2164 + this.hidden = true; 1.2165 + ]]></body> 1.2166 + </method> 1.2167 + <method name="onLinkClick"> 1.2168 + <body><![CDATA[ 1.2169 + // Open a new selected tab and close the current panel. 1.2170 + openUILinkIn(this._promolink.getAttribute("href"), "tab"); 1.2171 + this._panel.hidePopup(); 1.2172 + ]]></body> 1.2173 + </method> 1.2174 + <method name="handleEvent"> 1.2175 + <parameter name="event"/> 1.2176 + <body><![CDATA[ 1.2177 + if (event.type != "popupshowing" || event.target != this._panel) 1.2178 + return; 1.2179 + 1.2180 + // A previous notification may have unhidden this. 1.2181 + this.hidden = true; 1.2182 + 1.2183 + // Only handle supported notification panels. 1.2184 + if (!this._notificationType) { 1.2185 + return; 1.2186 + } 1.2187 + 1.2188 + let viewsLeft = this._viewsLeft; 1.2189 + if (viewsLeft) { 1.2190 + if (Services.prefs.prefHasUserValue("services.sync.username") && 1.2191 + this._notificationType != "addons-sync-disabled") { 1.2192 + // If the user has already setup Sync, don't show the notification. 1.2193 + this._viewsLeft = 0; 1.2194 + // Be sure to hide the panel, in case it was visible and the user 1.2195 + // decided to setup Sync after noticing it. 1.2196 + viewsLeft = 0; 1.2197 + // The panel is still hidden, just bail out. 1.2198 + return; 1.2199 + } 1.2200 + else { 1.2201 + this._viewsLeft = viewsLeft - 1; 1.2202 + } 1.2203 + 1.2204 + this._promolink.setAttribute("href", this._notificationLink); 1.2205 + this._promolink.value = gNavigatorBundle.getString("syncPromoNotification.learnMoreLinkText"); 1.2206 + 1.2207 + this.hidden = false; 1.2208 + 1.2209 + // HACK: The description element doesn't wrap correctly in panels, 1.2210 + // thus set a width on it, based on the available space, before 1.2211 + // setting its textContent. Then set its height as well, to 1.2212 + // fix wrong height calculation on Linux (bug 659578). 1.2213 + this._panel.addEventListener("popupshown", function panelShown() { 1.2214 + this._panel.removeEventListener("popupshown", panelShown, true); 1.2215 + // Previous popupShown events may close the panel or change 1.2216 + // its contents, so ensure this is still valid. 1.2217 + if (this._panel.state != "open" || !this._notificationType) 1.2218 + return; 1.2219 + this._promomessage.width = this._promomessage.getBoundingClientRect().width; 1.2220 + this._promomessage.firstChild.textContent = this._notificationMessage; 1.2221 + this._promomessage.height = this._promomessage.getBoundingClientRect().height; 1.2222 + }.bind(this), true); 1.2223 + } 1.2224 + ]]></body> 1.2225 + </method> 1.2226 + </implementation> 1.2227 + </binding> 1.2228 + 1.2229 + <binding id="toolbarbutton-badged" display="xul:button" 1.2230 + extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton"> 1.2231 + <content> 1.2232 + <children includes="observes|template|menupopup|panel|tooltip"/> 1.2233 + <xul:hbox class="toolbarbutton-badge-container" align="start" pack="end"> 1.2234 + <xul:hbox class="toolbarbutton-badge" xbl:inherits="badge"/> 1.2235 + <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/> 1.2236 + </xul:hbox> 1.2237 + <xul:label class="toolbarbutton-text" crop="right" flex="1" 1.2238 + xbl:inherits="value=label,accesskey,crop,wrap"/> 1.2239 + <xul:label class="toolbarbutton-multiline-text" flex="1" 1.2240 + xbl:inherits="xbl:text=label,accesskey,wrap"/> 1.2241 + </content> 1.2242 + </binding> 1.2243 + 1.2244 +</bindings>