browser/base/content/urlbarBindings.xml

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 <?xml version="1.0"?>
michael@0 2
michael@0 3 # -*- Mode: HTML -*-
michael@0 4 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 7
michael@0 8 <!DOCTYPE bindings [
michael@0 9 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
michael@0 10 %notificationDTD;
michael@0 11 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
michael@0 12 %browserDTD;
michael@0 13 ]>
michael@0 14
michael@0 15 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
michael@0 16 xmlns:html="http://www.w3.org/1999/xhtml"
michael@0 17 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
michael@0 18 xmlns:xbl="http://www.mozilla.org/xbl">
michael@0 19
michael@0 20 <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
michael@0 21
michael@0 22 <content sizetopopup="pref">
michael@0 23 <xul:hbox anonid="textbox-container"
michael@0 24 class="autocomplete-textbox-container urlbar-textbox-container"
michael@0 25 flex="1" xbl:inherits="focused">
michael@0 26 <children includes="image|deck|stack|box">
michael@0 27 <xul:image class="autocomplete-icon" allowevents="true"/>
michael@0 28 </children>
michael@0 29 <xul:hbox anonid="textbox-input-box"
michael@0 30 class="textbox-input-box urlbar-input-box"
michael@0 31 flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
michael@0 32 <children/>
michael@0 33 <html:input anonid="input"
michael@0 34 class="autocomplete-textbox urlbar-input textbox-input uri-element-right-align"
michael@0 35 allowevents="true"
michael@0 36 xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
michael@0 37 </xul:hbox>
michael@0 38 <children includes="hbox"/>
michael@0 39 </xul:hbox>
michael@0 40 <xul:dropmarker anonid="historydropmarker"
michael@0 41 class="autocomplete-history-dropmarker urlbar-history-dropmarker"
michael@0 42 allowevents="true"
michael@0 43 xbl:inherits="open,enablehistory,parentfocused=focused"/>
michael@0 44 <xul:popupset anonid="popupset"
michael@0 45 class="autocomplete-result-popupset"/>
michael@0 46 <children includes="toolbarbutton"/>
michael@0 47 </content>
michael@0 48
michael@0 49 <implementation implements="nsIObserver, nsIDOMEventListener">
michael@0 50 <constructor><![CDATA[
michael@0 51 this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
michael@0 52 .getService(Components.interfaces.nsIPrefService)
michael@0 53 .getBranch("browser.urlbar.");
michael@0 54
michael@0 55 this._prefs.addObserver("", this, false);
michael@0 56 this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
michael@0 57 this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
michael@0 58 this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
michael@0 59 this.timeout = this._prefs.getIntPref("delay");
michael@0 60 this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled");
michael@0 61 this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
michael@0 62 this._ignoreNextSelect = false;
michael@0 63
michael@0 64 this.inputField.controllers.insertControllerAt(0, this._copyCutController);
michael@0 65 this.inputField.addEventListener("mousedown", this, false);
michael@0 66 this.inputField.addEventListener("mousemove", this, false);
michael@0 67 this.inputField.addEventListener("mouseout", this, false);
michael@0 68 this.inputField.addEventListener("overflow", this, false);
michael@0 69 this.inputField.addEventListener("underflow", this, false);
michael@0 70
michael@0 71 try {
michael@0 72 if (this._prefs.getBoolPref("unifiedcomplete")) {
michael@0 73 this.setAttribute("autocompletesearch", "unifiedcomplete");
michael@0 74 }
michael@0 75 } catch (ex) {}
michael@0 76
michael@0 77 const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 78 var textBox = document.getAnonymousElementByAttribute(this,
michael@0 79 "anonid", "textbox-input-box");
michael@0 80 var cxmenu = document.getAnonymousElementByAttribute(textBox,
michael@0 81 "anonid", "input-box-contextmenu");
michael@0 82 var pasteAndGo;
michael@0 83 cxmenu.addEventListener("popupshowing", function() {
michael@0 84 if (!pasteAndGo)
michael@0 85 return;
michael@0 86 var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
michael@0 87 var enabled = controller.isCommandEnabled("cmd_paste");
michael@0 88 if (enabled)
michael@0 89 pasteAndGo.removeAttribute("disabled");
michael@0 90 else
michael@0 91 pasteAndGo.setAttribute("disabled", "true");
michael@0 92 }, false);
michael@0 93
michael@0 94 var insertLocation = cxmenu.firstChild;
michael@0 95 while (insertLocation.nextSibling &&
michael@0 96 insertLocation.getAttribute("cmd") != "cmd_paste")
michael@0 97 insertLocation = insertLocation.nextSibling;
michael@0 98 if (insertLocation) {
michael@0 99 pasteAndGo = document.createElement("menuitem");
michael@0 100 let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
michael@0 101 GetStringFromName("pasteAndGo.label");
michael@0 102 pasteAndGo.setAttribute("label", label);
michael@0 103 pasteAndGo.setAttribute("anonid", "paste-and-go");
michael@0 104 pasteAndGo.setAttribute("oncommand",
michael@0 105 "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();");
michael@0 106 cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
michael@0 107 }
michael@0 108 ]]></constructor>
michael@0 109
michael@0 110 <destructor><![CDATA[
michael@0 111 this._prefs.removeObserver("", this);
michael@0 112 this._prefs = null;
michael@0 113 this.inputField.controllers.removeController(this._copyCutController);
michael@0 114 this.inputField.removeEventListener("mousedown", this, false);
michael@0 115 this.inputField.removeEventListener("mousemove", this, false);
michael@0 116 this.inputField.removeEventListener("mouseout", this, false);
michael@0 117 this.inputField.removeEventListener("overflow", this, false);
michael@0 118 this.inputField.removeEventListener("underflow", this, false);
michael@0 119 ]]></destructor>
michael@0 120
michael@0 121 <field name="_value"></field>
michael@0 122
michael@0 123 <!--
michael@0 124 onBeforeValueGet is called by the base-binding's .value getter.
michael@0 125 It can return an object with a "value" property, to override the
michael@0 126 return value of the getter.
michael@0 127 -->
michael@0 128 <method name="onBeforeValueGet">
michael@0 129 <body><![CDATA[
michael@0 130 if (this.hasAttribute("actiontype"))
michael@0 131 return {value: this._value};
michael@0 132 return null;
michael@0 133 ]]></body>
michael@0 134 </method>
michael@0 135
michael@0 136 <!--
michael@0 137 onBeforeValueSet is called by the base-binding's .value setter.
michael@0 138 It should return the value that the setter should use.
michael@0 139 -->
michael@0 140 <method name="onBeforeValueSet">
michael@0 141 <parameter name="aValue"/>
michael@0 142 <body><![CDATA[
michael@0 143 this._value = aValue;
michael@0 144 var returnValue = aValue;
michael@0 145 var action = this._parseActionUrl(aValue);
michael@0 146 // Don't put back the action if we are invoked while override actions
michael@0 147 // is active.
michael@0 148 if (action && this._numNoActionsKeys <= 0) {
michael@0 149 returnValue = action.param;
michael@0 150 this.setAttribute("actiontype", action.type);
michael@0 151 } else {
michael@0 152 this.removeAttribute("actiontype");
michael@0 153 }
michael@0 154 return returnValue;
michael@0 155 ]]></body>
michael@0 156 </method>
michael@0 157
michael@0 158 <field name="_mayTrimURLs">true</field>
michael@0 159 <method name="trimValue">
michael@0 160 <parameter name="aURL"/>
michael@0 161 <body><![CDATA[
michael@0 162 // This method must not modify the given URL such that calling
michael@0 163 // nsIURIFixup::createFixupURI with the result will produce a different URI.
michael@0 164 return this._mayTrimURLs ? trimURL(aURL) : aURL;
michael@0 165 ]]></body>
michael@0 166 </method>
michael@0 167
michael@0 168 <field name="_formattingEnabled">true</field>
michael@0 169 <method name="formatValue">
michael@0 170 <body><![CDATA[
michael@0 171 if (!this._formattingEnabled || this.focused)
michael@0 172 return;
michael@0 173
michael@0 174 let controller = this.editor.selectionController;
michael@0 175 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
michael@0 176 selection.removeAllRanges();
michael@0 177
michael@0 178 let textNode = this.editor.rootElement.firstChild;
michael@0 179 let value = textNode.textContent;
michael@0 180
michael@0 181 let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
michael@0 182 if (protocol &&
michael@0 183 ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
michael@0 184 return;
michael@0 185 let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
michael@0 186 if (!matchedURL)
michael@0 187 return;
michael@0 188
michael@0 189 let [, preDomain, domain] = matchedURL;
michael@0 190 let baseDomain = domain;
michael@0 191 let subDomain = "";
michael@0 192 // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
michael@0 193 if (domain[0] != "[") {
michael@0 194 try {
michael@0 195 baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
michael@0 196 if (!domain.endsWith(baseDomain)) {
michael@0 197 // getBaseDomainFromHost converts its resultant to ACE.
michael@0 198 let IDNService = Cc["@mozilla.org/network/idn-service;1"]
michael@0 199 .getService(Ci.nsIIDNService);
michael@0 200 baseDomain = IDNService.convertACEtoUTF8(baseDomain);
michael@0 201 }
michael@0 202 } catch (e) {}
michael@0 203 }
michael@0 204 if (baseDomain != domain) {
michael@0 205 subDomain = domain.slice(0, -baseDomain.length);
michael@0 206 }
michael@0 207
michael@0 208 let rangeLength = preDomain.length + subDomain.length;
michael@0 209 if (rangeLength) {
michael@0 210 let range = document.createRange();
michael@0 211 range.setStart(textNode, 0);
michael@0 212 range.setEnd(textNode, rangeLength);
michael@0 213 selection.addRange(range);
michael@0 214 }
michael@0 215
michael@0 216 let startRest = preDomain.length + domain.length;
michael@0 217 if (startRest < value.length) {
michael@0 218 let range = document.createRange();
michael@0 219 range.setStart(textNode, startRest);
michael@0 220 range.setEnd(textNode, value.length);
michael@0 221 selection.addRange(range);
michael@0 222 }
michael@0 223 ]]></body>
michael@0 224 </method>
michael@0 225
michael@0 226 <method name="_clearFormatting">
michael@0 227 <body><![CDATA[
michael@0 228 if (!this._formattingEnabled)
michael@0 229 return;
michael@0 230
michael@0 231 let controller = this.editor.selectionController;
michael@0 232 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
michael@0 233 selection.removeAllRanges();
michael@0 234 ]]></body>
michael@0 235 </method>
michael@0 236
michael@0 237 <method name="handleRevert">
michael@0 238 <body><![CDATA[
michael@0 239 var isScrolling = this.popupOpen;
michael@0 240
michael@0 241 gBrowser.userTypedValue = null;
michael@0 242
michael@0 243 // don't revert to last valid url unless page is NOT loading
michael@0 244 // and user is NOT key-scrolling through autocomplete list
michael@0 245 if (!XULBrowserWindow.isBusy && !isScrolling) {
michael@0 246 URLBarSetURI();
michael@0 247
michael@0 248 // If the value isn't empty and the urlbar has focus, select the value.
michael@0 249 if (this.value && this.hasAttribute("focused"))
michael@0 250 this.select();
michael@0 251 }
michael@0 252
michael@0 253 // tell widget to revert to last typed text only if the user
michael@0 254 // was scrolling when they hit escape
michael@0 255 return !isScrolling;
michael@0 256 ]]></body>
michael@0 257 </method>
michael@0 258
michael@0 259 <method name="handleCommand">
michael@0 260 <parameter name="aTriggeringEvent"/>
michael@0 261 <body><![CDATA[
michael@0 262 if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2)
michael@0 263 return; // Do nothing for right clicks
michael@0 264
michael@0 265 var url = this.value;
michael@0 266 var mayInheritPrincipal = false;
michael@0 267 var postData = null;
michael@0 268
michael@0 269 var action = this._parseActionUrl(url);
michael@0 270 let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
michael@0 271
michael@0 272 let matchLastLocationChange = true;
michael@0 273 if (action) {
michael@0 274 url = action.param;
michael@0 275 if (this.hasAttribute("actiontype")) {
michael@0 276 if (action.type == "switchtab") {
michael@0 277 this.handleRevert();
michael@0 278 let prevTab = gBrowser.selectedTab;
michael@0 279 if (switchToTabHavingURI(url) &&
michael@0 280 isTabEmpty(prevTab))
michael@0 281 gBrowser.removeTab(prevTab);
michael@0 282 }
michael@0 283 return;
michael@0 284 }
michael@0 285 continueOperation.call(this);
michael@0 286 }
michael@0 287 else {
michael@0 288 this._canonizeURL(aTriggeringEvent, response => {
michael@0 289 [url, postData, mayInheritPrincipal] = response;
michael@0 290 if (url) {
michael@0 291 matchLastLocationChange = (lastLocationChange ==
michael@0 292 gBrowser.selectedBrowser.lastLocationChange);
michael@0 293 continueOperation.call(this);
michael@0 294 }
michael@0 295 });
michael@0 296 }
michael@0 297
michael@0 298 function continueOperation()
michael@0 299 {
michael@0 300 this.value = url;
michael@0 301 gBrowser.userTypedValue = url;
michael@0 302 try {
michael@0 303 addToUrlbarHistory(url);
michael@0 304 } catch (ex) {
michael@0 305 // Things may go wrong when adding url to session history,
michael@0 306 // but don't let that interfere with the loading of the url.
michael@0 307 Cu.reportError(ex);
michael@0 308 }
michael@0 309
michael@0 310 function loadCurrent() {
michael@0 311 let webnav = Ci.nsIWebNavigation;
michael@0 312 let flags = webnav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
michael@0 313 webnav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
michael@0 314 // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
michael@0 315 // inheriting the currently loaded document's principal, unless this
michael@0 316 // URL is marked as safe to inherit (e.g. came from a bookmark
michael@0 317 // keyword).
michael@0 318 if (!mayInheritPrincipal)
michael@0 319 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
michael@0 320 gBrowser.loadURIWithFlags(url, flags, null, null, postData);
michael@0 321 }
michael@0 322
michael@0 323 // Focus the content area before triggering loads, since if the load
michael@0 324 // occurs in a new tab, we want focus to be restored to the content
michael@0 325 // area when the current tab is re-selected.
michael@0 326 gBrowser.selectedBrowser.focus();
michael@0 327
michael@0 328 let isMouseEvent = aTriggeringEvent instanceof MouseEvent;
michael@0 329 let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey;
michael@0 330
michael@0 331 if (altEnter) {
michael@0 332 // XXX This was added a long time ago, and I'm not sure why it is
michael@0 333 // necessary. Alt+Enter's default action might cause a system beep,
michael@0 334 // or something like that?
michael@0 335 aTriggeringEvent.preventDefault();
michael@0 336 aTriggeringEvent.stopPropagation();
michael@0 337 }
michael@0 338
michael@0 339 // If the current tab is empty, ignore Alt+Enter (just reuse this tab)
michael@0 340 altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab);
michael@0 341
michael@0 342 if (isMouseEvent || altEnter) {
michael@0 343 // Use the standard UI link behaviors for clicks or Alt+Enter
michael@0 344 let where = "tab";
michael@0 345 if (isMouseEvent)
michael@0 346 where = whereToOpenLink(aTriggeringEvent, false, false);
michael@0 347
michael@0 348 if (where == "current") {
michael@0 349 if (matchLastLocationChange) {
michael@0 350 loadCurrent();
michael@0 351 }
michael@0 352 } else {
michael@0 353 this.handleRevert();
michael@0 354 let params = { allowThirdPartyFixup: true,
michael@0 355 postData: postData,
michael@0 356 initiatingDoc: document };
michael@0 357 openUILinkIn(url, where, params);
michael@0 358 }
michael@0 359 } else {
michael@0 360 if (matchLastLocationChange) {
michael@0 361 loadCurrent();
michael@0 362 }
michael@0 363 }
michael@0 364 }
michael@0 365 ]]></body>
michael@0 366 </method>
michael@0 367
michael@0 368 <method name="_canonizeURL">
michael@0 369 <parameter name="aTriggeringEvent"/>
michael@0 370 <parameter name="aCallback"/>
michael@0 371 <body><![CDATA[
michael@0 372 var url = this.value;
michael@0 373 if (!url) {
michael@0 374 aCallback(["", null, false]);
michael@0 375 return;
michael@0 376 }
michael@0 377
michael@0 378 // Only add the suffix when the URL bar value isn't already "URL-like",
michael@0 379 // and only if we get a keyboard event, to match user expectations.
michael@0 380 if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(url) &&
michael@0 381 (aTriggeringEvent instanceof KeyEvent)) {
michael@0 382 #ifdef XP_MACOSX
michael@0 383 let accel = aTriggeringEvent.metaKey;
michael@0 384 #else
michael@0 385 let accel = aTriggeringEvent.ctrlKey;
michael@0 386 #endif
michael@0 387 let shift = aTriggeringEvent.shiftKey;
michael@0 388
michael@0 389 let suffix = "";
michael@0 390
michael@0 391 switch (true) {
michael@0 392 case (accel && shift):
michael@0 393 suffix = ".org/";
michael@0 394 break;
michael@0 395 case (shift):
michael@0 396 suffix = ".net/";
michael@0 397 break;
michael@0 398 case (accel):
michael@0 399 try {
michael@0 400 suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
michael@0 401 if (suffix.charAt(suffix.length - 1) != "/")
michael@0 402 suffix += "/";
michael@0 403 } catch(e) {
michael@0 404 suffix = ".com/";
michael@0 405 }
michael@0 406 break;
michael@0 407 }
michael@0 408
michael@0 409 if (suffix) {
michael@0 410 // trim leading/trailing spaces (bug 233205)
michael@0 411 url = url.trim();
michael@0 412
michael@0 413 // Tack www. and suffix on. If user has appended directories, insert
michael@0 414 // suffix before them (bug 279035). Be careful not to get two slashes.
michael@0 415
michael@0 416 let firstSlash = url.indexOf("/");
michael@0 417
michael@0 418 if (firstSlash >= 0) {
michael@0 419 url = url.substring(0, firstSlash) + suffix +
michael@0 420 url.substring(firstSlash + 1);
michael@0 421 } else {
michael@0 422 url = url + suffix;
michael@0 423 }
michael@0 424
michael@0 425 url = "http://www." + url;
michael@0 426 }
michael@0 427 }
michael@0 428
michael@0 429 getShortcutOrURIAndPostData(url, data => {
michael@0 430 aCallback([data.url, data.postData, data.mayInheritPrincipal]);
michael@0 431 });
michael@0 432 ]]></body>
michael@0 433 </method>
michael@0 434
michael@0 435 <field name="_contentIsCropped">false</field>
michael@0 436
michael@0 437 <method name="_initURLTooltip">
michael@0 438 <body><![CDATA[
michael@0 439 if (this.focused || !this._contentIsCropped)
michael@0 440 return;
michael@0 441 this.inputField.setAttribute("tooltiptext", this.value);
michael@0 442 ]]></body>
michael@0 443 </method>
michael@0 444
michael@0 445 <method name="_hideURLTooltip">
michael@0 446 <body><![CDATA[
michael@0 447 this.inputField.removeAttribute("tooltiptext");
michael@0 448 ]]></body>
michael@0 449 </method>
michael@0 450
michael@0 451 <method name="onDragOver">
michael@0 452 <parameter name="aEvent"/>
michael@0 453 <body>
michael@0 454 var types = aEvent.dataTransfer.types;
michael@0 455 if (types.contains("application/x-moz-file") ||
michael@0 456 types.contains("text/x-moz-url") ||
michael@0 457 types.contains("text/uri-list") ||
michael@0 458 types.contains("text/unicode"))
michael@0 459 aEvent.preventDefault();
michael@0 460 </body>
michael@0 461 </method>
michael@0 462
michael@0 463 <method name="onDrop">
michael@0 464 <parameter name="aEvent"/>
michael@0 465 <body><![CDATA[
michael@0 466 let url = browserDragAndDrop.drop(aEvent, { })
michael@0 467
michael@0 468 // The URL bar automatically handles inputs with newline characters,
michael@0 469 // so we can get away with treating text/x-moz-url flavours as text/plain.
michael@0 470 if (url) {
michael@0 471 aEvent.preventDefault();
michael@0 472 this.value = url;
michael@0 473 SetPageProxyState("invalid");
michael@0 474 this.focus();
michael@0 475 try {
michael@0 476 urlSecurityCheck(url,
michael@0 477 gBrowser.contentPrincipal,
michael@0 478 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
michael@0 479 } catch (ex) {
michael@0 480 return;
michael@0 481 }
michael@0 482 this.handleCommand();
michael@0 483 }
michael@0 484 ]]></body>
michael@0 485 </method>
michael@0 486
michael@0 487 <method name="_getSelectedValueForClipboard">
michael@0 488 <body><![CDATA[
michael@0 489 // Grab the actual input field's value, not our value, which could include moz-action:
michael@0 490 var inputVal = this.inputField.value;
michael@0 491 var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd);
michael@0 492
michael@0 493 // If the selection doesn't start at the beginning or doesn't span the full domain or
michael@0 494 // the URL bar is modified, nothing else to do here.
michael@0 495 if (this.selectionStart > 0 || this.valueIsTyped)
michael@0 496 return selectedVal;
michael@0 497 // The selection doesn't span the full domain if it doesn't contain a slash and is
michael@0 498 // followed by some character other than a slash.
michael@0 499 if (!selectedVal.contains("/")) {
michael@0 500 let remainder = inputVal.replace(selectedVal, "");
michael@0 501 if (remainder != "" && remainder[0] != "/")
michael@0 502 return selectedVal;
michael@0 503 }
michael@0 504
michael@0 505 let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
michael@0 506
michael@0 507 let uri;
michael@0 508 try {
michael@0 509 uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
michael@0 510 } catch (e) {}
michael@0 511 if (!uri)
michael@0 512 return selectedVal;
michael@0 513
michael@0 514 // Only copy exposable URIs
michael@0 515 try {
michael@0 516 uri = uriFixup.createExposableURI(uri);
michael@0 517 } catch (ex) {}
michael@0 518
michael@0 519 // If the entire URL is selected, just use the actual loaded URI.
michael@0 520 if (inputVal == selectedVal) {
michael@0 521 // ... but only if isn't a javascript: or data: URI, since those
michael@0 522 // are hard to read when encoded
michael@0 523 if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
michael@0 524 // Parentheses are known to confuse third-party applications (bug 458565).
michael@0 525 selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
michael@0 526 }
michael@0 527
michael@0 528 return selectedVal;
michael@0 529 }
michael@0 530
michael@0 531 // Just the beginning of the URL is selected, check for a trimmed
michael@0 532 // value
michael@0 533 let spec = uri.spec;
michael@0 534 let trimmedSpec = this.trimValue(spec);
michael@0 535 if (spec != trimmedSpec) {
michael@0 536 // Prepend the portion that trimValue removed from the beginning.
michael@0 537 // This assumes trimValue will only truncate the URL at
michael@0 538 // the beginning or end (or both).
michael@0 539 let trimmedSegments = spec.split(trimmedSpec);
michael@0 540 selectedVal = trimmedSegments[0] + selectedVal;
michael@0 541 }
michael@0 542
michael@0 543 return selectedVal;
michael@0 544 ]]></body>
michael@0 545 </method>
michael@0 546
michael@0 547 <field name="_copyCutController"><![CDATA[
michael@0 548 ({
michael@0 549 urlbar: this,
michael@0 550 doCommand: function(aCommand) {
michael@0 551 var urlbar = this.urlbar;
michael@0 552 var val = urlbar._getSelectedValueForClipboard();
michael@0 553 if (!val)
michael@0 554 return;
michael@0 555
michael@0 556 if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
michael@0 557 let start = urlbar.selectionStart;
michael@0 558 let end = urlbar.selectionEnd;
michael@0 559 urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
michael@0 560 urlbar.inputField.value.substring(end);
michael@0 561 urlbar.selectionStart = urlbar.selectionEnd = start;
michael@0 562 urlbar.removeAttribute("actiontype");
michael@0 563 SetPageProxyState("invalid");
michael@0 564 }
michael@0 565
michael@0 566 Cc["@mozilla.org/widget/clipboardhelper;1"]
michael@0 567 .getService(Ci.nsIClipboardHelper)
michael@0 568 .copyString(val, document);
michael@0 569 },
michael@0 570 supportsCommand: function(aCommand) {
michael@0 571 switch (aCommand) {
michael@0 572 case "cmd_copy":
michael@0 573 case "cmd_cut":
michael@0 574 return true;
michael@0 575 }
michael@0 576 return false;
michael@0 577 },
michael@0 578 isCommandEnabled: function(aCommand) {
michael@0 579 return this.supportsCommand(aCommand) &&
michael@0 580 (aCommand != "cmd_cut" || !this.urlbar.readOnly) &&
michael@0 581 this.urlbar.selectionStart < this.urlbar.selectionEnd;
michael@0 582 },
michael@0 583 onEvent: function(aEventName) {}
michael@0 584 })
michael@0 585 ]]></field>
michael@0 586
michael@0 587 <method name="observe">
michael@0 588 <parameter name="aSubject"/>
michael@0 589 <parameter name="aTopic"/>
michael@0 590 <parameter name="aData"/>
michael@0 591 <body><![CDATA[
michael@0 592 if (aTopic == "nsPref:changed") {
michael@0 593 switch (aData) {
michael@0 594 case "clickSelectsAll":
michael@0 595 case "doubleClickSelectsAll":
michael@0 596 this[aData] = this._prefs.getBoolPref(aData);
michael@0 597 break;
michael@0 598 case "autoFill":
michael@0 599 this.completeDefaultIndex = this._prefs.getBoolPref(aData);
michael@0 600 break;
michael@0 601 case "delay":
michael@0 602 this.timeout = this._prefs.getIntPref(aData);
michael@0 603 break;
michael@0 604 case "formatting.enabled":
michael@0 605 this._formattingEnabled = this._prefs.getBoolPref(aData);
michael@0 606 break;
michael@0 607 case "trimURLs":
michael@0 608 this._mayTrimURLs = this._prefs.getBoolPref(aData);
michael@0 609 break;
michael@0 610 case "unifiedcomplete":
michael@0 611 let useUnifiedComplete = false;
michael@0 612 try {
michael@0 613 useUnifiedComplete = this._prefs.getBoolPref(aData);
michael@0 614 } catch (ex) {}
michael@0 615 this.setAttribute("autocompletesearch",
michael@0 616 useUnifiedComplete ? "unifiedcomplete"
michael@0 617 : "urlinline history");
michael@0 618 }
michael@0 619 }
michael@0 620 ]]></body>
michael@0 621 </method>
michael@0 622
michael@0 623 <method name="handleEvent">
michael@0 624 <parameter name="aEvent"/>
michael@0 625 <body><![CDATA[
michael@0 626 switch (aEvent.type) {
michael@0 627 case "mousedown":
michael@0 628 if (this.doubleClickSelectsAll &&
michael@0 629 aEvent.button == 0 && aEvent.detail == 2) {
michael@0 630 this.editor.selectAll();
michael@0 631 aEvent.preventDefault();
michael@0 632 }
michael@0 633 break;
michael@0 634 case "mousemove":
michael@0 635 this._initURLTooltip();
michael@0 636 break;
michael@0 637 case "mouseout":
michael@0 638 this._hideURLTooltip();
michael@0 639 break;
michael@0 640 case "overflow":
michael@0 641 this._contentIsCropped = true;
michael@0 642 break;
michael@0 643 case "underflow":
michael@0 644 this._contentIsCropped = false;
michael@0 645 this._hideURLTooltip();
michael@0 646 break;
michael@0 647 }
michael@0 648 ]]></body>
michael@0 649 </method>
michael@0 650
michael@0 651 <property name="textValue"
michael@0 652 onget="return this.value;">
michael@0 653 <setter>
michael@0 654 <![CDATA[
michael@0 655 try {
michael@0 656 val = losslessDecodeURI(makeURI(val));
michael@0 657 } catch (ex) { }
michael@0 658
michael@0 659 // Trim popup selected values, but never trim results coming from
michael@0 660 // autofill.
michael@0 661 if (this.popup.selectedIndex == -1)
michael@0 662 this._disableTrim = true;
michael@0 663 this.value = val;
michael@0 664 this._disableTrim = false;
michael@0 665
michael@0 666 // Completing a result should simulate the user typing the result, so
michael@0 667 // fire an input event.
michael@0 668 let evt = document.createEvent("UIEvents");
michael@0 669 evt.initUIEvent("input", true, false, window, 0);
michael@0 670 this.mIgnoreInput = true;
michael@0 671 this.dispatchEvent(evt);
michael@0 672 this.mIgnoreInput = false;
michael@0 673
michael@0 674 return this.value;
michael@0 675 ]]>
michael@0 676 </setter>
michael@0 677 </property>
michael@0 678
michael@0 679 <method name="_parseActionUrl">
michael@0 680 <parameter name="aUrl"/>
michael@0 681 <body><![CDATA[
michael@0 682 if (!aUrl.startsWith("moz-action:"))
michael@0 683 return null;
michael@0 684
michael@0 685 // url is in the format moz-action:ACTION,PARAM
michael@0 686 let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
michael@0 687 return {type: action, param: param};
michael@0 688 ]]></body>
michael@0 689 </method>
michael@0 690
michael@0 691 <field name="_numNoActionsKeys"><![CDATA[
michael@0 692 0
michael@0 693 ]]></field>
michael@0 694
michael@0 695 <method name="_clearNoActions">
michael@0 696 <parameter name="aURL"/>
michael@0 697 <body><![CDATA[
michael@0 698 this._numNoActionsKeys = 0;
michael@0 699 this.popup.removeAttribute("noactions");
michael@0 700 let action = this._parseActionUrl(this._value);
michael@0 701 if (action)
michael@0 702 this.setAttribute("actiontype", action.type);
michael@0 703 ]]></body>
michael@0 704 </method>
michael@0 705
michael@0 706 <method name="selectTextRange">
michael@0 707 <parameter name="aStartIndex"/>
michael@0 708 <parameter name="aEndIndex"/>
michael@0 709 <body><![CDATA[
michael@0 710 this._ignoreNextSelect = true;
michael@0 711 this.inputField.setSelectionRange(aStartIndex, aEndIndex);
michael@0 712 ]]></body>
michael@0 713 </method>
michael@0 714 </implementation>
michael@0 715
michael@0 716 <handlers>
michael@0 717 <handler event="keydown"><![CDATA[
michael@0 718 if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
michael@0 719 event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
michael@0 720 this.popup.selectedIndex >= 0) {
michael@0 721 this._numNoActionsKeys++;
michael@0 722 this.popup.setAttribute("noactions", "true");
michael@0 723 this.removeAttribute("actiontype");
michael@0 724 }
michael@0 725 ]]></handler>
michael@0 726
michael@0 727 <handler event="keyup"><![CDATA[
michael@0 728 if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
michael@0 729 event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
michael@0 730 this._numNoActionsKeys > 0) {
michael@0 731 this._numNoActionsKeys--;
michael@0 732 if (this._numNoActionsKeys == 0)
michael@0 733 this._clearNoActions();
michael@0 734 }
michael@0 735 ]]></handler>
michael@0 736
michael@0 737 <handler event="blur"><![CDATA[
michael@0 738 this._clearNoActions();
michael@0 739 this.formatValue();
michael@0 740 ]]></handler>
michael@0 741
michael@0 742 <handler event="dragstart" phase="capturing"><![CDATA[
michael@0 743 // Drag only if the gesture starts from the input field.
michael@0 744 if (event.originalTarget != this.inputField)
michael@0 745 return;
michael@0 746
michael@0 747 // Drag only if the entire value is selected and it's a valid URI.
michael@0 748 var isFullSelection = this.selectionStart == 0 &&
michael@0 749 this.selectionEnd == this.textLength;
michael@0 750 if (!isFullSelection ||
michael@0 751 this.getAttribute("pageproxystate") != "valid")
michael@0 752 return;
michael@0 753
michael@0 754 var urlString = content.location.href;
michael@0 755 var title = content.document.title || urlString;
michael@0 756 var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>";
michael@0 757
michael@0 758 var dt = event.dataTransfer;
michael@0 759 dt.setData("text/x-moz-url", urlString + "\n" + title);
michael@0 760 dt.setData("text/unicode", urlString);
michael@0 761 dt.setData("text/html", htmlString);
michael@0 762
michael@0 763 dt.effectAllowed = "copyLink";
michael@0 764 event.stopPropagation();
michael@0 765 ]]></handler>
michael@0 766
michael@0 767 <handler event="focus" phase="capturing"><![CDATA[
michael@0 768 this._hideURLTooltip();
michael@0 769 this._clearFormatting();
michael@0 770 ]]></handler>
michael@0 771
michael@0 772 <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
michael@0 773 <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
michael@0 774 <handler event="select"><![CDATA[
michael@0 775 if (this._ignoreNextSelect) {
michael@0 776 // If this select event is coming from autocomplete's selectTextRange,
michael@0 777 // then we don't need to adjust what's on the selection keyboard here,
michael@0 778 // but make sure to reset the flag since this should be a one-time
michael@0 779 // suppression.
michael@0 780 this._ignoreNextSelect = false;
michael@0 781 return;
michael@0 782 }
michael@0 783
michael@0 784 if (!Cc["@mozilla.org/widget/clipboard;1"]
michael@0 785 .getService(Ci.nsIClipboard)
michael@0 786 .supportsSelectionClipboard())
michael@0 787 return;
michael@0 788
michael@0 789 var val = this._getSelectedValueForClipboard();
michael@0 790 if (!val)
michael@0 791 return;
michael@0 792
michael@0 793 Cc["@mozilla.org/widget/clipboardhelper;1"]
michael@0 794 .getService(Ci.nsIClipboardHelper)
michael@0 795 .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard, document);
michael@0 796 ]]></handler>
michael@0 797 </handlers>
michael@0 798
michael@0 799 </binding>
michael@0 800
michael@0 801 <!-- Note: this binding is applied to the autocomplete popup used in the Search bar and in web page content -->
michael@0 802 <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup">
michael@0 803 <implementation>
michael@0 804 <method name="openAutocompletePopup">
michael@0 805 <parameter name="aInput"/>
michael@0 806 <parameter name="aElement"/>
michael@0 807 <body>
michael@0 808 <![CDATA[
michael@0 809 // initially the panel is hidden
michael@0 810 // to avoid impacting startup / new window performance
michael@0 811 aInput.popup.hidden = false;
michael@0 812
michael@0 813 // this method is defined on the base binding
michael@0 814 this._openAutocompletePopup(aInput, aElement);
michael@0 815 ]]></body>
michael@0 816 </method>
michael@0 817
michael@0 818 <method name="onPopupClick">
michael@0 819 <parameter name="aEvent"/>
michael@0 820 <body><![CDATA[
michael@0 821 // Ignore all right-clicks
michael@0 822 if (aEvent.button == 2)
michael@0 823 return;
michael@0 824
michael@0 825 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
michael@0 826
michael@0 827 // Check for unmodified left-click, and use default behavior
michael@0 828 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
michael@0 829 !aEvent.altKey && !aEvent.metaKey) {
michael@0 830 controller.handleEnter(true);
michael@0 831 return;
michael@0 832 }
michael@0 833
michael@0 834 // Check for middle-click or modified clicks on the search bar
michael@0 835 var searchBar = BrowserSearch.searchBar;
michael@0 836 if (searchBar && searchBar.textbox == this.mInput) {
michael@0 837 // Handle search bar popup clicks
michael@0 838 var search = controller.getValueAt(this.selectedIndex);
michael@0 839
michael@0 840 // close the autocomplete popup and revert the entered search term
michael@0 841 this.closePopup();
michael@0 842 controller.handleEscape();
michael@0 843
michael@0 844 // Fill in the search bar's value
michael@0 845 searchBar.value = search;
michael@0 846
michael@0 847 // open the search results according to the clicking subtlety
michael@0 848 var where = whereToOpenLink(aEvent, false, true);
michael@0 849 searchBar.doSearch(search, where);
michael@0 850 }
michael@0 851 ]]></body>
michael@0 852 </method>
michael@0 853 </implementation>
michael@0 854 </binding>
michael@0 855
michael@0 856 <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
michael@0 857 <implementation>
michael@0 858 <field name="_maxResults">0</field>
michael@0 859
michael@0 860 <field name="_bundle" readonly="true">
michael@0 861 Cc["@mozilla.org/intl/stringbundle;1"].
michael@0 862 getService(Ci.nsIStringBundleService).
michael@0 863 createBundle("chrome://browser/locale/places/places.properties");
michael@0 864 </field>
michael@0 865
michael@0 866 <property name="maxResults" readonly="true">
michael@0 867 <getter>
michael@0 868 <![CDATA[
michael@0 869 if (!this._maxResults) {
michael@0 870 var prefService =
michael@0 871 Components.classes["@mozilla.org/preferences-service;1"]
michael@0 872 .getService(Components.interfaces.nsIPrefBranch);
michael@0 873 this._maxResults = prefService.getIntPref("browser.urlbar.maxRichResults");
michael@0 874 }
michael@0 875 return this._maxResults;
michael@0 876 ]]>
michael@0 877 </getter>
michael@0 878 </property>
michael@0 879
michael@0 880 <method name="openAutocompletePopup">
michael@0 881 <parameter name="aInput"/>
michael@0 882 <parameter name="aElement"/>
michael@0 883 <body>
michael@0 884 <![CDATA[
michael@0 885 // initially the panel is hidden
michael@0 886 // to avoid impacting startup / new window performance
michael@0 887 aInput.popup.hidden = false;
michael@0 888
michael@0 889 // this method is defined on the base binding
michael@0 890 this._openAutocompletePopup(aInput, aElement);
michael@0 891 ]]></body>
michael@0 892 </method>
michael@0 893
michael@0 894 <method name="onPopupClick">
michael@0 895 <parameter name="aEvent"/>
michael@0 896 <body>
michael@0 897 <![CDATA[
michael@0 898 // Ignore right-clicks
michael@0 899 if (aEvent.button == 2)
michael@0 900 return;
michael@0 901
michael@0 902 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
michael@0 903
michael@0 904 // Check for unmodified left-click, and use default behavior
michael@0 905 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
michael@0 906 !aEvent.altKey && !aEvent.metaKey) {
michael@0 907 controller.handleEnter(true);
michael@0 908 return;
michael@0 909 }
michael@0 910
michael@0 911 // Check for middle-click or modified clicks on the URL bar
michael@0 912 if (gURLBar && this.mInput == gURLBar) {
michael@0 913 var url = controller.getValueAt(this.selectedIndex);
michael@0 914
michael@0 915 // close the autocomplete popup and revert the entered address
michael@0 916 this.closePopup();
michael@0 917 controller.handleEscape();
michael@0 918
michael@0 919 // Check if this is meant to be an action
michael@0 920 let action = this.mInput._parseActionUrl(url);
michael@0 921 if (action) {
michael@0 922 if (action.type == "switchtab")
michael@0 923 url = action.param;
michael@0 924 else
michael@0 925 return;
michael@0 926 }
michael@0 927
michael@0 928 // respect the usual clicking subtleties
michael@0 929 openUILink(url, aEvent);
michael@0 930 }
michael@0 931 ]]>
michael@0 932 </body>
michael@0 933 </method>
michael@0 934
michael@0 935 <method name="createResultLabel">
michael@0 936 <parameter name="aTitle"/>
michael@0 937 <parameter name="aUrl"/>
michael@0 938 <parameter name="aType"/>
michael@0 939 <body>
michael@0 940 <![CDATA[
michael@0 941 var label = aTitle + " " + aUrl;
michael@0 942 // convert aType (ex: "ac-result-type-<aType>") to text to be spoke aloud
michael@0 943 // by screen readers. convert "tag" and "bookmark" to the localized versions,
michael@0 944 // but don't do anything for "favicon" (the default)
michael@0 945 if (aType != "favicon") {
michael@0 946 label += " " + this._bundle.GetStringFromName(aType + "ResultLabel");
michael@0 947 }
michael@0 948 return label;
michael@0 949 ]]>
michael@0 950 </body>
michael@0 951 </method>
michael@0 952
michael@0 953 </implementation>
michael@0 954 </binding>
michael@0 955
michael@0 956 <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
michael@0 957 <content align="start">
michael@0 958 <xul:image class="popup-notification-icon"
michael@0 959 xbl:inherits="popupid,src=icon"/>
michael@0 960 <xul:vbox flex="1">
michael@0 961 <xul:description class="popup-notification-description addon-progress-description"
michael@0 962 xbl:inherits="xbl:text=label"/>
michael@0 963 <xul:spacer flex="1"/>
michael@0 964 <xul:hbox align="center">
michael@0 965 <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/>
michael@0 966 <xul:button anonid="cancel" class="popup-progress-cancel" oncommand="document.getBindingParent(this).cancel()"/>
michael@0 967 </xul:hbox>
michael@0 968 <xul:label anonid="progresstext" class="popup-progress-label"/>
michael@0 969 <xul:hbox class="popup-notification-button-container"
michael@0 970 pack="end" align="center">
michael@0 971 <xul:button anonid="button"
michael@0 972 class="popup-notification-menubutton"
michael@0 973 type="menu-button"
michael@0 974 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
michael@0 975 <xul:menupopup anonid="menupopup"
michael@0 976 xbl:inherits="oncommand=menucommand">
michael@0 977 <children/>
michael@0 978 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
michael@0 979 label="&closeNotificationItem.label;"
michael@0 980 xbl:inherits="oncommand=closeitemcommand"/>
michael@0 981 </xul:menupopup>
michael@0 982 </xul:button>
michael@0 983 </xul:hbox>
michael@0 984 </xul:vbox>
michael@0 985 <xul:vbox pack="start">
michael@0 986 <xul:toolbarbutton anonid="closebutton"
michael@0 987 class="messageCloseButton close-icon popup-notification-closebutton tabbable"
michael@0 988 xbl:inherits="oncommand=closebuttoncommand"
michael@0 989 tooltiptext="&closeNotification.tooltip;"/>
michael@0 990 </xul:vbox>
michael@0 991 </content>
michael@0 992 <implementation>
michael@0 993 <constructor><![CDATA[
michael@0 994 this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip"));
michael@0 995
michael@0 996 this.notification.options.installs.forEach(function(aInstall) {
michael@0 997 aInstall.addListener(this);
michael@0 998 }, this);
michael@0 999
michael@0 1000 // Calling updateProgress can sometimes cause this notification to be
michael@0 1001 // removed in the middle of refreshing the notification panel which
michael@0 1002 // makes the panel get refreshed again. Just initialise to the
michael@0 1003 // undetermined state and then schedule a proper check at the next
michael@0 1004 // opportunity
michael@0 1005 this.setProgress(0, -1);
michael@0 1006 this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0);
michael@0 1007 ]]></constructor>
michael@0 1008
michael@0 1009 <destructor><![CDATA[
michael@0 1010 this.destroy();
michael@0 1011 ]]></destructor>
michael@0 1012
michael@0 1013 <field name="progressmeter" readonly="true">
michael@0 1014 document.getAnonymousElementByAttribute(this, "anonid", "progressmeter");
michael@0 1015 </field>
michael@0 1016 <field name="progresstext" readonly="true">
michael@0 1017 document.getAnonymousElementByAttribute(this, "anonid", "progresstext");
michael@0 1018 </field>
michael@0 1019 <field name="cancelbtn" readonly="true">
michael@0 1020 document.getAnonymousElementByAttribute(this, "anonid", "cancel");
michael@0 1021 </field>
michael@0 1022 <field name="DownloadUtils" readonly="true">
michael@0 1023 let utils = {};
michael@0 1024 Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils);
michael@0 1025 utils.DownloadUtils;
michael@0 1026 </field>
michael@0 1027
michael@0 1028 <method name="destroy">
michael@0 1029 <body><![CDATA[
michael@0 1030 this.notification.options.installs.forEach(function(aInstall) {
michael@0 1031 aInstall.removeListener(this);
michael@0 1032 }, this);
michael@0 1033 clearTimeout(this._updateProgressTimeout);
michael@0 1034 ]]></body>
michael@0 1035 </method>
michael@0 1036
michael@0 1037 <method name="setProgress">
michael@0 1038 <parameter name="aProgress"/>
michael@0 1039 <parameter name="aMaxProgress"/>
michael@0 1040 <body><![CDATA[
michael@0 1041 if (aMaxProgress == -1) {
michael@0 1042 this.progressmeter.mode = "undetermined";
michael@0 1043 }
michael@0 1044 else {
michael@0 1045 this.progressmeter.mode = "determined";
michael@0 1046 this.progressmeter.value = (aProgress * 100) / aMaxProgress;
michael@0 1047 }
michael@0 1048
michael@0 1049 let now = Date.now();
michael@0 1050
michael@0 1051 if (!this.notification.lastUpdate) {
michael@0 1052 this.notification.lastUpdate = now;
michael@0 1053 this.notification.lastProgress = aProgress;
michael@0 1054 return;
michael@0 1055 }
michael@0 1056
michael@0 1057 let delta = now - this.notification.lastUpdate;
michael@0 1058 if ((delta < 400) && (aProgress < aMaxProgress))
michael@0 1059 return;
michael@0 1060
michael@0 1061 delta /= 1000;
michael@0 1062
michael@0 1063 // This code is taken from nsDownloadManager.cpp
michael@0 1064 let speed = (aProgress - this.notification.lastProgress) / delta;
michael@0 1065 if (this.notification.speed)
michael@0 1066 speed = speed * 0.9 + this.notification.speed * 0.1;
michael@0 1067
michael@0 1068 this.notification.lastUpdate = now;
michael@0 1069 this.notification.lastProgress = aProgress;
michael@0 1070 this.notification.speed = speed;
michael@0 1071
michael@0 1072 let status = null;
michael@0 1073 [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last);
michael@0 1074 this.progresstext.value = status;
michael@0 1075 ]]></body>
michael@0 1076 </method>
michael@0 1077
michael@0 1078 <method name="cancel">
michael@0 1079 <body><![CDATA[
michael@0 1080 // Cache these as cancelling the installs will remove this
michael@0 1081 // notification which will drop these references
michael@0 1082 let browser = this.notification.browser;
michael@0 1083 let contentWindow = this.notification.options.contentWindow;
michael@0 1084 let sourceURI = this.notification.options.sourceURI;
michael@0 1085
michael@0 1086 let installs = this.notification.options.installs;
michael@0 1087 installs.forEach(function(aInstall) {
michael@0 1088 try {
michael@0 1089 aInstall.cancel();
michael@0 1090 }
michael@0 1091 catch (e) {
michael@0 1092 // Cancel will throw if the download has already failed
michael@0 1093 }
michael@0 1094 }, this);
michael@0 1095
michael@0 1096 let anchorID = "addons-notification-icon";
michael@0 1097 let notificationID = "addon-install-cancelled";
michael@0 1098 let messageString = gNavigatorBundle.getString("addonDownloadCancelled");
michael@0 1099 messageString = PluralForm.get(installs.length, messageString);
michael@0 1100 let buttonText = gNavigatorBundle.getString("addonDownloadRestart");
michael@0 1101 buttonText = PluralForm.get(installs.length, buttonText);
michael@0 1102
michael@0 1103 let action = {
michael@0 1104 label: buttonText,
michael@0 1105 accessKey: gNavigatorBundle.getString("addonDownloadRestart.accessKey"),
michael@0 1106 callback: function() {
michael@0 1107 let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
michael@0 1108 getService(Ci.amIWebInstallListener);
michael@0 1109 if (weblistener.onWebInstallRequested(contentWindow, sourceURI,
michael@0 1110 installs, installs.length)) {
michael@0 1111 installs.forEach(function(aInstall) {
michael@0 1112 aInstall.install();
michael@0 1113 });
michael@0 1114 }
michael@0 1115 }
michael@0 1116 };
michael@0 1117
michael@0 1118 PopupNotifications.show(browser, notificationID, messageString,
michael@0 1119 anchorID, action);
michael@0 1120 ]]></body>
michael@0 1121 </method>
michael@0 1122
michael@0 1123 <method name="updateProgress">
michael@0 1124 <body><![CDATA[
michael@0 1125 let downloadingCount = 0;
michael@0 1126 let progress = 0;
michael@0 1127 let maxProgress = 0;
michael@0 1128
michael@0 1129 this.notification.options.installs.forEach(function(aInstall) {
michael@0 1130 if (aInstall.maxProgress == -1)
michael@0 1131 maxProgress = -1;
michael@0 1132 progress += aInstall.progress;
michael@0 1133 if (maxProgress >= 0)
michael@0 1134 maxProgress += aInstall.maxProgress;
michael@0 1135 if (aInstall.state < AddonManager.STATE_DOWNLOADED)
michael@0 1136 downloadingCount++;
michael@0 1137 });
michael@0 1138
michael@0 1139 if (downloadingCount == 0) {
michael@0 1140 this.destroy();
michael@0 1141 PopupNotifications.remove(this.notification);
michael@0 1142 }
michael@0 1143 else {
michael@0 1144 this.setProgress(progress, maxProgress);
michael@0 1145 }
michael@0 1146 ]]></body>
michael@0 1147 </method>
michael@0 1148
michael@0 1149 <method name="onDownloadProgress">
michael@0 1150 <body><![CDATA[
michael@0 1151 this.updateProgress();
michael@0 1152 ]]></body>
michael@0 1153 </method>
michael@0 1154
michael@0 1155 <method name="onDownloadFailed">
michael@0 1156 <body><![CDATA[
michael@0 1157 this.updateProgress();
michael@0 1158 ]]></body>
michael@0 1159 </method>
michael@0 1160
michael@0 1161 <method name="onDownloadCancelled">
michael@0 1162 <body><![CDATA[
michael@0 1163 this.updateProgress();
michael@0 1164 ]]></body>
michael@0 1165 </method>
michael@0 1166
michael@0 1167 <method name="onDownloadEnded">
michael@0 1168 <body><![CDATA[
michael@0 1169 this.updateProgress();
michael@0 1170 ]]></body>
michael@0 1171 </method>
michael@0 1172 </implementation>
michael@0 1173 </binding>
michael@0 1174
michael@0 1175 <binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
michael@0 1176 <content align="start">
michael@0 1177
michael@0 1178 <xul:image class="popup-notification-icon"
michael@0 1179 xbl:inherits="popupid,src=icon"/>
michael@0 1180
michael@0 1181 <xul:vbox flex="1">
michael@0 1182 <xul:vbox anonid="identity-deck">
michael@0 1183 <xul:vbox flex="1" pack="center"> <!-- 1: add an email -->
michael@0 1184 <html:input type="email" anonid="email" required="required" size="30"/>
michael@0 1185 <xul:description anonid="newidentitydesc"/>
michael@0 1186 <xul:spacer flex="1"/>
michael@0 1187 <xul:label class="text-link custom-link small-margin" anonid="chooseemail" hidden="true"/>
michael@0 1188 </xul:vbox>
michael@0 1189 <xul:vbox flex="1" hidden="true"> <!-- 2: choose an email -->
michael@0 1190 <xul:description anonid="chooseidentitydesc"/>
michael@0 1191 <xul:radiogroup anonid="identities">
michael@0 1192 </xul:radiogroup>
michael@0 1193 <xul:label class="text-link custom-link" anonid="newemail"/>
michael@0 1194 </xul:vbox>
michael@0 1195 </xul:vbox>
michael@0 1196 <xul:hbox class="popup-notification-button-container"
michael@0 1197 pack="end" align="center">
michael@0 1198 <xul:label anonid="tos" class="text-link" hidden="true"/>
michael@0 1199 <xul:label anonid="privacypolicy" class="text-link" hidden="true"/>
michael@0 1200 <xul:spacer flex="1"/>
michael@0 1201 <xul:image anonid="throbber" src="chrome://browser/skin/tabbrowser/loading.png"
michael@0 1202 style="visibility:hidden" width="16" height="16"/>
michael@0 1203 <xul:button anonid="button"
michael@0 1204 type="menu-button"
michael@0 1205 class="popup-notification-menubutton"
michael@0 1206 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
michael@0 1207 <xul:menupopup anonid="menupopup"
michael@0 1208 xbl:inherits="oncommand=menucommand">
michael@0 1209 <children/>
michael@0 1210 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
michael@0 1211 label="&closeNotificationItem.label;"
michael@0 1212 xbl:inherits="oncommand=closeitemcommand"/>
michael@0 1213 </xul:menupopup>
michael@0 1214 </xul:button>
michael@0 1215 </xul:hbox>
michael@0 1216 </xul:vbox>
michael@0 1217 <xul:vbox pack="start">
michael@0 1218 <xul:toolbarbutton anonid="closebutton"
michael@0 1219 class="messageCloseButton close-icon popup-notification-closebutton tabbable"
michael@0 1220 xbl:inherits="oncommand=closebuttoncommand"
michael@0 1221 tooltiptext="&closeNotification.tooltip;"/>
michael@0 1222 </xul:vbox>
michael@0 1223 </content>
michael@0 1224 <implementation>
michael@0 1225 <constructor><![CDATA[
michael@0 1226 // this.notification.options.identity is used to pass identity-specific info to the binding
michael@0 1227 let origin = this.identity.origin
michael@0 1228
michael@0 1229 // Populate text
michael@0 1230 this.emailField.placeholder = gNavigatorBundle.
michael@0 1231 getString("identity.newIdentity.email.placeholder");
michael@0 1232 this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
michael@0 1233 "identity.newIdentity.description", [origin]);
michael@0 1234 this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
michael@0 1235 "identity.chooseIdentity.description", [origin]);
michael@0 1236
michael@0 1237 // Show optional terms of service and privacy policy links
michael@0 1238 this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService");
michael@0 1239 this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy");
michael@0 1240
michael@0 1241 // Populate the list of identities to choose from. The origin is used to provide
michael@0 1242 // better suggestions.
michael@0 1243 let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin);
michael@0 1244
michael@0 1245 this._populateIdentityList(identities);
michael@0 1246
michael@0 1247 if (typeof this.step == "undefined") {
michael@0 1248 // First opening of this notification
michael@0 1249 // Show the add email pane (0) if there are no existing identities otherwise show the list
michael@0 1250 this.step = "result" in identities && identities.result.length ? 1 : 0;
michael@0 1251 } else {
michael@0 1252 // Already opened so restore previous state
michael@0 1253 if (this.identity.typedEmail) {
michael@0 1254 this.emailField.value = this.identity.typedEmail;
michael@0 1255 }
michael@0 1256 if (this.identity.selected) {
michael@0 1257 // If the user already chose an identity then update the UI to reflect that
michael@0 1258 this.onIdentitySelected();
michael@0 1259 }
michael@0 1260 // Update the view for the step
michael@0 1261 this.step = this.step;
michael@0 1262 }
michael@0 1263
michael@0 1264 // Fire notification with the chosen identity when main button is clicked
michael@0 1265 this.button.addEventListener("command", this._onButtonCommand.bind(this), true);
michael@0 1266
michael@0 1267 // Do the same if enter is pressed in the email field
michael@0 1268 this.emailField.addEventListener("keypress", function emailFieldKeypress(aEvent) {
michael@0 1269 if (aEvent.keyCode != aEvent.DOM_VK_RETURN)
michael@0 1270 return;
michael@0 1271 this._onButtonCommand(aEvent);
michael@0 1272 }.bind(this));
michael@0 1273
michael@0 1274 this.addEmailLink.value = gNavigatorBundle.getString("identity.newIdentity.label");
michael@0 1275 this.addEmailLink.accessKey = gNavigatorBundle.getString("identity.newIdentity.accessKey");
michael@0 1276 this.addEmailLink.addEventListener("click", function addEmailClick(evt) {
michael@0 1277 this.step = 0;
michael@0 1278 }.bind(this));
michael@0 1279
michael@0 1280 this.chooseEmailLink.value = gNavigatorBundle.getString("identity.chooseIdentity.label");
michael@0 1281 this.chooseEmailLink.hidden = !("result" in identities && identities.result.length);
michael@0 1282 this.chooseEmailLink.addEventListener("click", function chooseEmailClick(evt) {
michael@0 1283 this.step = 1;
michael@0 1284 }.bind(this));
michael@0 1285
michael@0 1286 this.emailField.addEventListener("blur", function onEmailBlur() {
michael@0 1287 this.identity.typedEmail = this.emailField.value;
michael@0 1288 }.bind(this));
michael@0 1289 ]]></constructor>
michael@0 1290
michael@0 1291 <field name="SignInToWebsiteUX" readonly="true">
michael@0 1292 let sitw = {};
michael@0 1293 Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw);
michael@0 1294 sitw.SignInToWebsiteUX;
michael@0 1295 </field>
michael@0 1296
michael@0 1297 <field name="newIdentityDesc" readonly="true">
michael@0 1298 document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc");
michael@0 1299 </field>
michael@0 1300
michael@0 1301 <field name="chooseIdentityDesc" readonly="true">
michael@0 1302 document.getAnonymousElementByAttribute(this, "anonid", "chooseidentitydesc");
michael@0 1303 </field>
michael@0 1304
michael@0 1305 <field name="identityList" readonly="true">
michael@0 1306 document.getAnonymousElementByAttribute(this, "anonid", "identities");
michael@0 1307 </field>
michael@0 1308
michael@0 1309 <field name="emailField" readonly="true">
michael@0 1310 document.getAnonymousElementByAttribute(this, "anonid", "email");
michael@0 1311 </field>
michael@0 1312
michael@0 1313 <field name="addEmailLink" readonly="true">
michael@0 1314 document.getAnonymousElementByAttribute(this, "anonid", "newemail");
michael@0 1315 </field>
michael@0 1316
michael@0 1317 <field name="chooseEmailLink" readonly="true">
michael@0 1318 document.getAnonymousElementByAttribute(this, "anonid", "chooseemail");
michael@0 1319 </field>
michael@0 1320
michael@0 1321 <field name="throbber" readonly="true">
michael@0 1322 document.getAnonymousElementByAttribute(this, "anonid", "throbber");
michael@0 1323 </field>
michael@0 1324
michael@0 1325 <field name="identity" readonly="true">
michael@0 1326 this.notification.options.identity;
michael@0 1327 </field>
michael@0 1328
michael@0 1329 <!-- persist the state on the identity object so we can re-create the
michael@0 1330 notification state upon re-opening -->
michael@0 1331 <property name="step">
michael@0 1332 <getter>
michael@0 1333 return this.identity.step;
michael@0 1334 </getter>
michael@0 1335 <setter><![CDATA[
michael@0 1336 let deck = document.getAnonymousElementByAttribute(this, "anonid", "identity-deck");
michael@0 1337 for (let i = 0; i < deck.children.length; i++) {
michael@0 1338 deck.children[i].hidden = (val != i);
michael@0 1339 }
michael@0 1340 this.identity.step = val;
michael@0 1341 switch (val) {
michael@0 1342 case 0:
michael@0 1343 this.emailField.focus();
michael@0 1344 break;
michael@0 1345 }]]>
michael@0 1346 </setter>
michael@0 1347 </property>
michael@0 1348
michael@0 1349 <method name="onIdentitySelected">
michael@0 1350 <body><![CDATA[
michael@0 1351 this.throbber.style.visibility = "visible";
michael@0 1352 this.button.disabled = true;
michael@0 1353 this.emailField.value = this.identity.selected
michael@0 1354 this.emailField.disabled = true;
michael@0 1355 this.identityList.disabled = true;
michael@0 1356 ]]></body>
michael@0 1357 </method>
michael@0 1358
michael@0 1359 <method name="_populateLink">
michael@0 1360 <parameter name="aURL"/>
michael@0 1361 <parameter name="aLinkId"/>
michael@0 1362 <parameter name="aStringId"/>
michael@0 1363 <body><![CDATA[
michael@0 1364 if (aURL) {
michael@0 1365 // Show optional link to aURL
michael@0 1366 let link = document.getAnonymousElementByAttribute(this, "anonid", aLinkId);
michael@0 1367 link.value = gNavigatorBundle.getString(aStringId);
michael@0 1368 link.href = aURL;
michael@0 1369 link.hidden = false;
michael@0 1370 }
michael@0 1371 ]]></body>
michael@0 1372 </method>
michael@0 1373
michael@0 1374 <method name="_populateIdentityList">
michael@0 1375 <parameter name="aIdentities"/>
michael@0 1376 <body><![CDATA[
michael@0 1377 let foundLastUsed = false;
michael@0 1378 let lastUsed = this.identity.selected || aIdentities.lastUsed;
michael@0 1379 for (let id in aIdentities.result) {
michael@0 1380 let label = aIdentities.result[id];
michael@0 1381 let opt = this.identityList.appendItem(label);
michael@0 1382 if (label == lastUsed) {
michael@0 1383 this.identityList.selectedItem = opt;
michael@0 1384 foundLastUsed = true;
michael@0 1385 }
michael@0 1386 }
michael@0 1387 if (!foundLastUsed) {
michael@0 1388 this.identityList.selectedIndex = -1;
michael@0 1389 }
michael@0 1390 ]]></body>
michael@0 1391 </method>
michael@0 1392
michael@0 1393 <method name="_onButtonCommand">
michael@0 1394 <parameter name="aEvent"/>
michael@0 1395 <body><![CDATA[
michael@0 1396 if (aEvent.target != aEvent.currentTarget)
michael@0 1397 return;
michael@0 1398 let chosenId;
michael@0 1399 switch (this.step) {
michael@0 1400 case 0:
michael@0 1401 aEvent.stopPropagation();
michael@0 1402 if (!this.emailField.validity.valid) {
michael@0 1403 this.emailField.focus();
michael@0 1404 return;
michael@0 1405 }
michael@0 1406 chosenId = this.emailField.value;
michael@0 1407 break;
michael@0 1408 case 1:
michael@0 1409 aEvent.stopPropagation();
michael@0 1410 let selectedItem = this.identityList.selectedItem
michael@0 1411 chosenId = selectedItem ? selectedItem.label : null;
michael@0 1412 if (!chosenId)
michael@0 1413 return;
michael@0 1414 break;
michael@0 1415 default:
michael@0 1416 throw new Error("Unknown case");
michael@0 1417 return;
michael@0 1418 }
michael@0 1419 // Actually select the identity
michael@0 1420 this.SignInToWebsiteUX.selectIdentity(this.identity.rpId, chosenId);
michael@0 1421 this.identity.selected = chosenId;
michael@0 1422 this.onIdentitySelected();
michael@0 1423 ]]></body>
michael@0 1424 </method>
michael@0 1425
michael@0 1426 </implementation>
michael@0 1427 </binding>
michael@0 1428
michael@0 1429 <binding id="plugin-popupnotification-center-item">
michael@0 1430 <content align="center">
michael@0 1431 <xul:vbox pack="center" anonid="itemBox" class="itemBox">
michael@0 1432 <xul:description anonid="center-item-label" class="center-item-label" />
michael@0 1433 <xul:hbox flex="1" pack="start" align="center" anonid="center-item-warning">
michael@0 1434 <xul:image anonid="center-item-warning-icon" class="center-item-warning-icon"/>
michael@0 1435 <xul:label anonid="center-item-warning-label"/>
michael@0 1436 <xul:label anonid="center-item-link" value="&checkForUpdates;" class="text-link"/>
michael@0 1437 </xul:hbox>
michael@0 1438 </xul:vbox>
michael@0 1439 <xul:vbox pack="center">
michael@0 1440 <xul:menulist class="center-item-menulist"
michael@0 1441 anonid="center-item-menulist">
michael@0 1442 <xul:menupopup>
michael@0 1443 <xul:menuitem anonid="allownow" value="allownow"
michael@0 1444 label="&pluginActivateNow.label;" />
michael@0 1445 <xul:menuitem anonid="allowalways" value="allowalways"
michael@0 1446 label="&pluginActivateAlways.label;" />
michael@0 1447 <xul:menuitem anonid="block" value="block"
michael@0 1448 label="&pluginBlockNow.label;" />
michael@0 1449 </xul:menupopup>
michael@0 1450 </xul:menulist>
michael@0 1451 </xul:vbox>
michael@0 1452 </content>
michael@0 1453 <resources>
michael@0 1454 <stylesheet src="chrome://global/skin/notification.css"/>
michael@0 1455 </resources>
michael@0 1456 <implementation>
michael@0 1457 <constructor><![CDATA[
michael@0 1458 document.getAnonymousElementByAttribute(this, "anonid", "center-item-label").value = this.action.pluginName;
michael@0 1459
michael@0 1460 let curState = "block";
michael@0 1461 if (this.action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
michael@0 1462 if (this.action.pluginPermissionType == Ci.nsIPermissionManager.EXPIRE_SESSION) {
michael@0 1463 curState = "allownow";
michael@0 1464 }
michael@0 1465 else {
michael@0 1466 curState = "allowalways";
michael@0 1467 }
michael@0 1468 }
michael@0 1469 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").value = curState;
michael@0 1470
michael@0 1471 let warningString = "";
michael@0 1472 let linkString = "";
michael@0 1473
michael@0 1474 let link = document.getAnonymousElementByAttribute(this, "anonid", "center-item-link");
michael@0 1475
michael@0 1476 let url;
michael@0 1477 let linkHandler;
michael@0 1478
michael@0 1479 if (this.action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) {
michael@0 1480 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true;
michael@0 1481 warningString = gNavigatorBundle.getString("pluginActivateDisabled.label");
michael@0 1482 linkString = gNavigatorBundle.getString("pluginActivateDisabled.manage");
michael@0 1483 linkHandler = function(event) {
michael@0 1484 event.preventDefault();
michael@0 1485 gPluginHandler.managePlugins();
michael@0 1486 };
michael@0 1487 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-icon").hidden = true;
michael@0 1488 }
michael@0 1489 else {
michael@0 1490 url = this.action.detailsLink;
michael@0 1491
michael@0 1492 switch (this.action.blocklistState) {
michael@0 1493 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
michael@0 1494 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning").hidden = true;
michael@0 1495 break;
michael@0 1496 case Ci.nsIBlocklistService.STATE_BLOCKED:
michael@0 1497 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true;
michael@0 1498 warningString = gNavigatorBundle.getString("pluginActivateBlocked.label");
michael@0 1499 linkString = gNavigatorBundle.getString("pluginActivate.learnMore");
michael@0 1500 break;
michael@0 1501 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
michael@0 1502 warningString = gNavigatorBundle.getString("pluginActivateOutdated.label");
michael@0 1503 linkString = gNavigatorBundle.getString("pluginActivate.updateLabel");
michael@0 1504 break;
michael@0 1505 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
michael@0 1506 warningString = gNavigatorBundle.getString("pluginActivateVulnerable.label");
michael@0 1507 linkString = gNavigatorBundle.getString("pluginActivate.riskLabel");
michael@0 1508 break;
michael@0 1509 }
michael@0 1510 }
michael@0 1511 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString;
michael@0 1512
michael@0 1513 if (url || linkHandler) {
michael@0 1514 link.value = linkString;
michael@0 1515 if (url) {
michael@0 1516 link.href = url;
michael@0 1517 }
michael@0 1518 if (linkHandler) {
michael@0 1519 link.addEventListener("click", linkHandler, false);
michael@0 1520 }
michael@0 1521 }
michael@0 1522 else {
michael@0 1523 link.hidden = true;
michael@0 1524 }
michael@0 1525 ]]></constructor>
michael@0 1526 <property name="value">
michael@0 1527 <getter>
michael@0 1528 return document.getAnonymousElementByAttribute(this, "anonid",
michael@0 1529 "center-item-menulist").value;
michael@0 1530 </getter>
michael@0 1531 <setter><!-- This should be used only in automated tests -->
michael@0 1532 document.getAnonymousElementByAttribute(this, "anonid",
michael@0 1533 "center-item-menulist").value = val;
michael@0 1534 </setter>
michael@0 1535 </property>
michael@0 1536 </implementation>
michael@0 1537 </binding>
michael@0 1538
michael@0 1539 <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
michael@0 1540 <content align="start" style="width: &pluginNotification.width;;">
michael@0 1541 <xul:vbox flex="1" align="stretch" class="popup-notification-main-box"
michael@0 1542 xbl:inherits="popupid">
michael@0 1543 <xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start">
michael@0 1544 <xul:description class="click-to-play-plugins-outer-description" flex="1">
michael@0 1545 <html:span anonid="click-to-play-plugins-notification-description" />
michael@0 1546 <xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" />
michael@0 1547 </xul:description>
michael@0 1548 <xul:toolbarbutton anonid="closebutton"
michael@0 1549 class="messageCloseButton popup-notification-closebutton tabbable close-icon"
michael@0 1550 xbl:inherits="oncommand=closebuttoncommand"
michael@0 1551 tooltiptext="&closeNotification.tooltip;"/>
michael@0 1552 </xul:hbox>
michael@0 1553 <xul:grid anonid="click-to-play-plugins-notification-center-box"
michael@0 1554 class="click-to-play-plugins-notification-center-box">
michael@0 1555 <xul:columns>
michael@0 1556 <xul:column flex="1"/>
michael@0 1557 <xul:column/>
michael@0 1558 </xul:columns>
michael@0 1559 <xul:rows>
michael@0 1560 <children includes="row"/>
michael@0 1561 <xul:hbox pack="start" anonid="plugin-notification-showbox">
michael@0 1562 <xul:button label="&pluginNotification.showAll.label;"
michael@0 1563 accesskey="&pluginNotification.showAll.accesskey;"
michael@0 1564 class="plugin-notification-showbutton"
michael@0 1565 oncommand="document.getBindingParent(this)._setState(2)"/>
michael@0 1566 </xul:hbox>
michael@0 1567 </xul:rows>
michael@0 1568 </xul:grid>
michael@0 1569 <xul:hbox anonid="button-container"
michael@0 1570 class="click-to-play-plugins-notification-button-container"
michael@0 1571 pack="center" align="center">
michael@0 1572 <xul:button anonid="primarybutton"
michael@0 1573 class="click-to-play-popup-button"
michael@0 1574 oncommand="document.getBindingParent(this)._onButton(this)"
michael@0 1575 flex="1"/>
michael@0 1576 <xul:button anonid="secondarybutton"
michael@0 1577 class="click-to-play-popup-button"
michael@0 1578 oncommand="document.getBindingParent(this)._onButton(this);"
michael@0 1579 flex="1"/>
michael@0 1580 </xul:hbox>
michael@0 1581 <xul:box hidden="true">
michael@0 1582 <children/>
michael@0 1583 </xul:box>
michael@0 1584 </xul:vbox>
michael@0 1585 </content>
michael@0 1586 <resources>
michael@0 1587 <stylesheet src="chrome://global/skin/notification.css"/>
michael@0 1588 </resources>
michael@0 1589 <implementation>
michael@0 1590 <field name="_states">
michael@0 1591 ({SINGLE: 0, MULTI_COLLAPSED: 1, MULTI_EXPANDED: 2})
michael@0 1592 </field>
michael@0 1593 <field name="_primaryButton">
michael@0 1594 document.getAnonymousElementByAttribute(this, "anonid", "primarybutton");
michael@0 1595 </field>
michael@0 1596 <field name="_secondaryButton">
michael@0 1597 document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton")
michael@0 1598 </field>
michael@0 1599 <field name="_buttonContainer">
michael@0 1600 document.getAnonymousElementByAttribute(this, "anonid", "button-container")
michael@0 1601 </field>
michael@0 1602 <field name="_brandShortName">
michael@0 1603 document.getElementById("bundle_brand").getString("brandShortName")
michael@0 1604 </field>
michael@0 1605 <field name="_items">[]</field>
michael@0 1606 <constructor><![CDATA[
michael@0 1607 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 1608 let sortedActions = [];
michael@0 1609 for (let action of this.notification.options.pluginData.values()) {
michael@0 1610 sortedActions.push(action);
michael@0 1611 }
michael@0 1612 sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName));
michael@0 1613
michael@0 1614 for (let action of sortedActions) {
michael@0 1615 let item = document.createElementNS(XUL_NS, "row");
michael@0 1616 item.setAttribute("class", "plugin-popupnotification-centeritem");
michael@0 1617 item.action = action;
michael@0 1618 this.appendChild(item);
michael@0 1619 this._items.push(item);
michael@0 1620 }
michael@0 1621 switch (this._items.length) {
michael@0 1622 case 0:
michael@0 1623 PopupNotifications._dismiss();
michael@0 1624 break;
michael@0 1625 case 1:
michael@0 1626 this._setState(this._states.SINGLE);
michael@0 1627 break;
michael@0 1628 default:
michael@0 1629 if (this.notification.options.primaryPlugin) {
michael@0 1630 this._setState(this._states.MULTI_COLLAPSED);
michael@0 1631 } else {
michael@0 1632 this._setState(this._states.MULTI_EXPANDED);
michael@0 1633 }
michael@0 1634 }
michael@0 1635 ]]></constructor>
michael@0 1636 <method name="_setState">
michael@0 1637 <parameter name="state" />
michael@0 1638 <body><![CDATA[
michael@0 1639 var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box");
michael@0 1640
michael@0 1641 if (this._states.SINGLE == state) {
michael@0 1642 grid.hidden = true;
michael@0 1643 this._setupSingleState();
michael@0 1644 return;
michael@0 1645 }
michael@0 1646
michael@0 1647 let host = gPluginHandler._getHostFromPrincipal(this.notification.browser.contentWindow.document.nodePrincipal);
michael@0 1648 this._setupDescription("pluginActivateMultiple.message", null, host);
michael@0 1649
michael@0 1650 var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox");
michael@0 1651
michael@0 1652 var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties");
michael@0 1653 this._primaryButton.label = dialogStrings.GetStringFromName("button-accept");
michael@0 1654 this._primaryButton.setAttribute("default", "true");
michael@0 1655
michael@0 1656 this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel");
michael@0 1657 this._primaryButton.setAttribute("action", "_multiAccept");
michael@0 1658 this._secondaryButton.setAttribute("action", "_cancel");
michael@0 1659
michael@0 1660 grid.hidden = false;
michael@0 1661
michael@0 1662 if (this._states.MULTI_COLLAPSED == state) {
michael@0 1663 for (let child of this.childNodes) {
michael@0 1664 if (child.tagName != "row") {
michael@0 1665 continue;
michael@0 1666 }
michael@0 1667 child.hidden = this.notification.options.primaryPlugin !=
michael@0 1668 child.action.permissionString;
michael@0 1669 }
michael@0 1670 showBox.hidden = false;
michael@0 1671 }
michael@0 1672 else {
michael@0 1673 for (let child of this.childNodes) {
michael@0 1674 if (child.tagName != "row") {
michael@0 1675 continue;
michael@0 1676 }
michael@0 1677 child.hidden = false;
michael@0 1678 }
michael@0 1679 showBox.hidden = true;
michael@0 1680 }
michael@0 1681 this._setupLink(null);
michael@0 1682 ]]></body>
michael@0 1683 </method>
michael@0 1684 <method name="_setupSingleState">
michael@0 1685 <body><![CDATA[
michael@0 1686 var action = this._items[0].action;
michael@0 1687 var host = action.pluginPermissionHost;
michael@0 1688
michael@0 1689 let label, linkLabel, linkUrl, button1, button2;
michael@0 1690
michael@0 1691 if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
michael@0 1692 button1 = {
michael@0 1693 label: "pluginBlockNow.label",
michael@0 1694 accesskey: "pluginBlockNow.accesskey",
michael@0 1695 action: "_singleBlock"
michael@0 1696 };
michael@0 1697 button2 = {
michael@0 1698 label: "pluginContinue.label",
michael@0 1699 accesskey: "pluginContinue.accesskey",
michael@0 1700 action: "_singleContinue",
michael@0 1701 default: true
michael@0 1702 };
michael@0 1703 switch (action.blocklistState) {
michael@0 1704 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
michael@0 1705 label = "pluginEnabled.message";
michael@0 1706 linkLabel = "pluginActivate.learnMore";
michael@0 1707 break;
michael@0 1708
michael@0 1709 case Ci.nsIBlocklistService.STATE_BLOCKED:
michael@0 1710 Cu.reportError(Error("Cannot happen!"));
michael@0 1711 break;
michael@0 1712
michael@0 1713 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
michael@0 1714 label = "pluginEnabledOutdated.message";
michael@0 1715 linkLabel = "pluginActivate.updateLabel";
michael@0 1716 break;
michael@0 1717
michael@0 1718 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
michael@0 1719 label = "pluginEnabledVulnerable.message";
michael@0 1720 linkLabel = "pluginActivate.riskLabel"
michael@0 1721 break;
michael@0 1722
michael@0 1723 default:
michael@0 1724 Cu.reportError(Error("Unexpected blocklist state"));
michael@0 1725 }
michael@0 1726 }
michael@0 1727 else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) {
michael@0 1728 let linkElement =
michael@0 1729 document.getAnonymousElementByAttribute(
michael@0 1730 this, "anonid", "click-to-play-plugins-notification-link");
michael@0 1731 linkElement.textContent = gNavigatorBundle.getString("pluginActivateDisabled.manage");
michael@0 1732 linkElement.setAttribute("onclick", "gPluginHandler.managePlugins()");
michael@0 1733
michael@0 1734 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
michael@0 1735 descElement.textContent = gNavigatorBundle.getFormattedString(
michael@0 1736 "pluginActivateDisabled.message", [action.pluginName, this._brandShortName]) + " ";
michael@0 1737 this._buttonContainer.hidden = true;
michael@0 1738 return;
michael@0 1739 }
michael@0 1740 else if (action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
michael@0 1741 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
michael@0 1742 descElement.textContent = gNavigatorBundle.getFormattedString(
michael@0 1743 "pluginActivateBlocked.message", [action.pluginName, this._brandShortName]) + " ";
michael@0 1744 this._setupLink("pluginActivate.learnMore", action.detailsLink);
michael@0 1745 this._buttonContainer.hidden = true;
michael@0 1746 return;
michael@0 1747 }
michael@0 1748 else {
michael@0 1749 button1 = {
michael@0 1750 label: "pluginActivateNow.label",
michael@0 1751 accesskey: "pluginActivateNow.accesskey",
michael@0 1752 action: "_singleActivateNow"
michael@0 1753 };
michael@0 1754 button2 = {
michael@0 1755 label: "pluginActivateAlways.label",
michael@0 1756 accesskey: "pluginActivateAlways.accesskey",
michael@0 1757 action: "_singleActivateAlways"
michael@0 1758 };
michael@0 1759 switch (action.blocklistState) {
michael@0 1760 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
michael@0 1761 label = "pluginActivateNew.message";
michael@0 1762 linkLabel = "pluginActivate.learnMore";
michael@0 1763 button2.default = true;
michael@0 1764 break;
michael@0 1765
michael@0 1766 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
michael@0 1767 label = "pluginActivateOutdated.message";
michael@0 1768 linkLabel = "pluginActivate.updateLabel";
michael@0 1769 button1.default = true;
michael@0 1770 break;
michael@0 1771
michael@0 1772 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
michael@0 1773 label = "pluginActivateVulnerable.message";
michael@0 1774 linkLabel = "pluginActivate.riskLabel"
michael@0 1775 button1.default = true;
michael@0 1776 break;
michael@0 1777
michael@0 1778 default:
michael@0 1779 Cu.reportError(Error("Unexpected blocklist state"));
michael@0 1780 }
michael@0 1781 }
michael@0 1782 this._setupDescription(label, action.pluginName, host);
michael@0 1783 this._setupLink(linkLabel, action.detailsLink);
michael@0 1784
michael@0 1785 this._primaryButton.label = gNavigatorBundle.getString(button1.label);
michael@0 1786 this._primaryButton.accesskey = gNavigatorBundle.getString(button1.accesskey);
michael@0 1787 this._primaryButton.setAttribute("action", button1.action);
michael@0 1788
michael@0 1789 this._secondaryButton.label = gNavigatorBundle.getString(button2.label);
michael@0 1790 this._secondaryButton.accesskey = gNavigatorBundle.getString(button2.accesskey);
michael@0 1791 this._secondaryButton.setAttribute("action", button2.action);
michael@0 1792 if (button1.default) {
michael@0 1793 this._primaryButton.setAttribute("default", "true");
michael@0 1794 }
michael@0 1795 else if (button2.default) {
michael@0 1796 this._secondaryButton.setAttribute("default", "true");
michael@0 1797 }
michael@0 1798 ]]></body>
michael@0 1799 </method>
michael@0 1800 <method name="_setupDescription">
michael@0 1801 <parameter name="baseString" />
michael@0 1802 <parameter name="pluginName" /> <!-- null for the multiple-plugin case -->
michael@0 1803 <parameter name="host" />
michael@0 1804 <body><![CDATA[
michael@0 1805 var bsn = this._brandShortName;
michael@0 1806 var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
michael@0 1807 while (span.lastChild) {
michael@0 1808 span.removeChild(span.lastChild);
michael@0 1809 }
michael@0 1810
michael@0 1811 var args = ["__host__", this._brandShortName];
michael@0 1812 if (pluginName) {
michael@0 1813 args.unshift(pluginName);
michael@0 1814 }
michael@0 1815 var bases = gNavigatorBundle.getFormattedString(baseString, args).
michael@0 1816 split("__host__", 2);
michael@0 1817
michael@0 1818 span.appendChild(document.createTextNode(bases[0]));
michael@0 1819 var hostSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em");
michael@0 1820 hostSpan.appendChild(document.createTextNode(host));
michael@0 1821 span.appendChild(hostSpan);
michael@0 1822 span.appendChild(document.createTextNode(bases[1] + " "));
michael@0 1823 ]]></body>
michael@0 1824 </method>
michael@0 1825 <method name="_setupLink">
michael@0 1826 <parameter name="linkString"/>
michael@0 1827 <parameter name="linkUrl" />
michael@0 1828 <body><![CDATA[
michael@0 1829 var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link");
michael@0 1830 if (!linkString || !linkUrl) {
michael@0 1831 link.hidden = true;
michael@0 1832 return;
michael@0 1833 }
michael@0 1834
michael@0 1835 link.hidden = false;
michael@0 1836 link.textContent = gNavigatorBundle.getString(linkString);
michael@0 1837 link.href = linkUrl;
michael@0 1838 ]]></body>
michael@0 1839 </method>
michael@0 1840 <method name="_onButton">
michael@0 1841 <parameter name="aButton" />
michael@0 1842 <body><![CDATA[
michael@0 1843 let methodName = aButton.getAttribute("action");
michael@0 1844 this[methodName]();
michael@0 1845 ]]></body>
michael@0 1846 </method>
michael@0 1847 <method name="_singleActivateNow">
michael@0 1848 <body><![CDATA[
michael@0 1849 gPluginHandler._updatePluginPermission(this.notification,
michael@0 1850 this._items[0].action,
michael@0 1851 "allownow");
michael@0 1852 this._cancel();
michael@0 1853 ]]></body>
michael@0 1854 </method>
michael@0 1855 <method name="_singleBlock">
michael@0 1856 <body><![CDATA[
michael@0 1857 gPluginHandler._updatePluginPermission(this.notification,
michael@0 1858 this._items[0].action,
michael@0 1859 "block");
michael@0 1860 this._cancel();
michael@0 1861 ]]></body>
michael@0 1862 </method>
michael@0 1863 <method name="_singleActivateAlways">
michael@0 1864 <body><![CDATA[
michael@0 1865 gPluginHandler._updatePluginPermission(this.notification,
michael@0 1866 this._items[0].action,
michael@0 1867 "allowalways");
michael@0 1868 this._cancel();
michael@0 1869 ]]></body>
michael@0 1870 </method>
michael@0 1871 <method name="_singleContinue">
michael@0 1872 <body><![CDATA[
michael@0 1873 gPluginHandler._updatePluginPermission(this.notification,
michael@0 1874 this._items[0].action,
michael@0 1875 "continue");
michael@0 1876 this._cancel();
michael@0 1877 ]]></body>
michael@0 1878 </method>
michael@0 1879 <method name="_multiAccept">
michael@0 1880 <body><![CDATA[
michael@0 1881 for (let item of this._items) {
michael@0 1882 let action = item.action;
michael@0 1883 if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED ||
michael@0 1884 action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
michael@0 1885 continue;
michael@0 1886 }
michael@0 1887 gPluginHandler._updatePluginPermission(this.notification,
michael@0 1888 item.action, item.value);
michael@0 1889 }
michael@0 1890 this._cancel();
michael@0 1891 ]]></body>
michael@0 1892 </method>
michael@0 1893 <method name="_cancel">
michael@0 1894 <body><![CDATA[
michael@0 1895 PopupNotifications._dismiss();
michael@0 1896 ]]></body>
michael@0 1897 </method>
michael@0 1898 <method name="_accept">
michael@0 1899 <parameter name="aEvent" />
michael@0 1900 <body><![CDATA[
michael@0 1901 if (aEvent.defaultPrevented)
michael@0 1902 return;
michael@0 1903 aEvent.preventDefault();
michael@0 1904 if (this._primaryButton.getAttribute("default") == "true") {
michael@0 1905 this._primaryButton.click();
michael@0 1906 }
michael@0 1907 else if (this._secondaryButton.getAttribute("default") == "true") {
michael@0 1908 this._secondaryButton.click();
michael@0 1909 }
michael@0 1910 ]]></body>
michael@0 1911 </method>
michael@0 1912 </implementation>
michael@0 1913 <handlers>
michael@0 1914 <!-- The _accept method checks for .defaultPrevented so that if focus is in a button,
michael@0 1915 enter activates the button and not this default action -->
michael@0 1916 <handler event="keypress" keycode="VK_RETURN" group="system" action="this._accept(event);"/>
michael@0 1917 </handlers>
michael@0 1918 </binding>
michael@0 1919
michael@0 1920 <binding id="splitmenu">
michael@0 1921 <content>
michael@0 1922 <xul:hbox anonid="menuitem" flex="1"
michael@0 1923 class="splitmenu-menuitem"
michael@0 1924 xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/>
michael@0 1925 <xul:menu anonid="menu" class="splitmenu-menu"
michael@0 1926 xbl:inherits="disabled,_moz-menuactive=active"
michael@0 1927 oncommand="event.stopPropagation();">
michael@0 1928 <children includes="menupopup"/>
michael@0 1929 </xul:menu>
michael@0 1930 </content>
michael@0 1931
michael@0 1932 <implementation implements="nsIDOMEventListener">
michael@0 1933 <constructor><![CDATA[
michael@0 1934 this._parentMenupopup.addEventListener("DOMMenuItemActive", this, false);
michael@0 1935 this._parentMenupopup.addEventListener("popuphidden", this, false);
michael@0 1936 ]]></constructor>
michael@0 1937
michael@0 1938 <destructor><![CDATA[
michael@0 1939 this._parentMenupopup.removeEventListener("DOMMenuItemActive", this, false);
michael@0 1940 this._parentMenupopup.removeEventListener("popuphidden", this, false);
michael@0 1941 ]]></destructor>
michael@0 1942
michael@0 1943 <field name="menuitem" readonly="true">
michael@0 1944 document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
michael@0 1945 </field>
michael@0 1946 <field name="menu" readonly="true">
michael@0 1947 document.getAnonymousElementByAttribute(this, "anonid", "menu");
michael@0 1948 </field>
michael@0 1949
michael@0 1950 <field name="_menuDelay">600</field>
michael@0 1951
michael@0 1952 <field name="_parentMenupopup"><![CDATA[
michael@0 1953 this._getParentMenupopup(this);
michael@0 1954 ]]></field>
michael@0 1955
michael@0 1956 <method name="_getParentMenupopup">
michael@0 1957 <parameter name="aNode"/>
michael@0 1958 <body><![CDATA[
michael@0 1959 let node = aNode.parentNode;
michael@0 1960 while (node) {
michael@0 1961 if (node.localName == "menupopup")
michael@0 1962 break;
michael@0 1963 node = node.parentNode;
michael@0 1964 }
michael@0 1965 return node;
michael@0 1966 ]]></body>
michael@0 1967 </method>
michael@0 1968
michael@0 1969 <method name="handleEvent">
michael@0 1970 <parameter name="event"/>
michael@0 1971 <body><![CDATA[
michael@0 1972 switch (event.type) {
michael@0 1973 case "DOMMenuItemActive":
michael@0 1974 if (this.getAttribute("active") == "true" &&
michael@0 1975 event.target != this &&
michael@0 1976 this._getParentMenupopup(event.target) == this._parentMenupopup)
michael@0 1977 this.removeAttribute("active");
michael@0 1978 break;
michael@0 1979 case "popuphidden":
michael@0 1980 if (event.target == this._parentMenupopup)
michael@0 1981 this.removeAttribute("active");
michael@0 1982 break;
michael@0 1983 }
michael@0 1984 ]]></body>
michael@0 1985 </method>
michael@0 1986 </implementation>
michael@0 1987
michael@0 1988 <handlers>
michael@0 1989 <handler event="mouseover"><![CDATA[
michael@0 1990 if (this.getAttribute("active") != "true") {
michael@0 1991 this.setAttribute("active", "true");
michael@0 1992
michael@0 1993 let event = document.createEvent("Events");
michael@0 1994 event.initEvent("DOMMenuItemActive", true, false);
michael@0 1995 this.dispatchEvent(event);
michael@0 1996
michael@0 1997 if (this.getAttribute("disabled") != "true") {
michael@0 1998 let self = this;
michael@0 1999 setTimeout(function () {
michael@0 2000 if (self.getAttribute("active") == "true")
michael@0 2001 self.menu.open = true;
michael@0 2002 }, this._menuDelay);
michael@0 2003 }
michael@0 2004 }
michael@0 2005 ]]></handler>
michael@0 2006
michael@0 2007 <handler event="popupshowing"><![CDATA[
michael@0 2008 if (event.target == this.firstChild &&
michael@0 2009 this._parentMenupopup._currentPopup)
michael@0 2010 this._parentMenupopup._currentPopup.hidePopup();
michael@0 2011 ]]></handler>
michael@0 2012
michael@0 2013 <handler event="click" phase="capturing"><![CDATA[
michael@0 2014 if (this.getAttribute("disabled") == "true") {
michael@0 2015 // Prevent the command from being carried out
michael@0 2016 event.stopPropagation();
michael@0 2017 return;
michael@0 2018 }
michael@0 2019
michael@0 2020 let node = event.originalTarget;
michael@0 2021 while (true) {
michael@0 2022 if (node == this.menuitem)
michael@0 2023 break;
michael@0 2024 if (node == this)
michael@0 2025 return;
michael@0 2026 node = node.parentNode;
michael@0 2027 }
michael@0 2028
michael@0 2029 this._parentMenupopup.hidePopup();
michael@0 2030 ]]></handler>
michael@0 2031 </handlers>
michael@0 2032 </binding>
michael@0 2033
michael@0 2034 <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
michael@0 2035 <implementation>
michael@0 2036 <constructor><![CDATA[
michael@0 2037 this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
michael@0 2038 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
michael@0 2039 // 592424 is fixed
michael@0 2040 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
michael@0 2041 ]]></constructor>
michael@0 2042 </implementation>
michael@0 2043 </binding>
michael@0 2044
michael@0 2045 <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic">
michael@0 2046 <implementation>
michael@0 2047 <constructor><![CDATA[
michael@0 2048 this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
michael@0 2049 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
michael@0 2050 // 592424 is fixed
michael@0 2051 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
michael@0 2052 ]]></constructor>
michael@0 2053 </implementation>
michael@0 2054 </binding>
michael@0 2055
michael@0 2056 <binding id="promobox">
michael@0 2057 <content>
michael@0 2058 <xul:hbox class="panel-promo-box" align="start" flex="1">
michael@0 2059 <xul:hbox align="center" flex="1">
michael@0 2060 <xul:image class="panel-promo-icon"/>
michael@0 2061 <xul:description anonid="promo-message" class="panel-promo-message" flex="1">
michael@0 2062 <xul:description anonid="promo-link"
michael@0 2063 class="plain text-link inline-link"
michael@0 2064 onclick="document.getBindingParent(this).onLinkClick();"/>
michael@0 2065 </xul:description>
michael@0 2066 </xul:hbox>
michael@0 2067 <xul:toolbarbutton class="panel-promo-closebutton close-icon"
michael@0 2068 oncommand="document.getBindingParent(this).onCloseButtonCommand();"
michael@0 2069 tooltiptext="&closeNotification.tooltip;"/>
michael@0 2070 </xul:hbox>
michael@0 2071 </content>
michael@0 2072
michael@0 2073 <implementation implements="nsIDOMEventListener">
michael@0 2074 <constructor><![CDATA[
michael@0 2075 this._panel.addEventListener("popupshowing", this, false);
michael@0 2076 ]]></constructor>
michael@0 2077
michael@0 2078 <destructor><![CDATA[
michael@0 2079 this._panel.removeEventListener("popupshowing", this, false);
michael@0 2080 ]]></destructor>
michael@0 2081
michael@0 2082 <field name="_panel" readonly="true"><![CDATA[
michael@0 2083 let node = this.parentNode;
michael@0 2084 while(node && node.localName != "panel") {
michael@0 2085 node = node.parentNode;
michael@0 2086 }
michael@0 2087 node;
michael@0 2088 ]]></field>
michael@0 2089 <field name="_promomessage" readonly="true">
michael@0 2090 document.getAnonymousElementByAttribute(this, "anonid", "promo-message");
michael@0 2091 </field>
michael@0 2092 <field name="_promolink" readonly="true">
michael@0 2093 document.getAnonymousElementByAttribute(this, "anonid", "promo-link");
michael@0 2094 </field>
michael@0 2095 <field name="_brandBundle" readonly="true">
michael@0 2096 Services.strings.createBundle("chrome://branding/locale/brand.properties");
michael@0 2097 </field>
michael@0 2098 <property name="_viewsLeftMap">
michael@0 2099 <getter><![CDATA[
michael@0 2100 try {
michael@0 2101 return JSON.parse(Services.prefs.getCharPref("browser.syncPromoViewsLeftMap"));
michael@0 2102 } catch (ex) {}
michael@0 2103 return {};
michael@0 2104 ]]></getter>
michael@0 2105 </property>
michael@0 2106 <property name="_viewsLeft">
michael@0 2107 <getter><![CDATA[
michael@0 2108 let views = 5;
michael@0 2109 let map = this._viewsLeftMap;
michael@0 2110 if (this._notificationType in map) {
michael@0 2111 views = map[this._notificationType];
michael@0 2112 }
michael@0 2113 return views;
michael@0 2114 ]]></getter>
michael@0 2115 <setter><![CDATA[
michael@0 2116 let map = this._viewsLeftMap;
michael@0 2117 map[this._notificationType] = val;
michael@0 2118 Services.prefs.setCharPref("browser.syncPromoViewsLeftMap",
michael@0 2119 JSON.stringify(map));
michael@0 2120 return val;
michael@0 2121 ]]></setter>
michael@0 2122 </property>
michael@0 2123 <property name="_notificationType">
michael@0 2124 <getter><![CDATA[
michael@0 2125 // Use the popupid attribute to identify the notification type,
michael@0 2126 // otherwise just rely on the panel id for common arrowpanels.
michael@0 2127 let type = this._panel.firstChild.getAttribute("popupid") ||
michael@0 2128 this._panel.id;
michael@0 2129 if (type.startsWith("password-"))
michael@0 2130 return "passwords";
michael@0 2131 if (type == "editBookmarkPanel")
michael@0 2132 return "bookmarks";
michael@0 2133 if (type == "addon-install-complete") {
michael@0 2134 if (!Services.prefs.prefHasUserValue("services.sync.username"))
michael@0 2135 return "addons";
michael@0 2136 if (!Services.prefs.getBoolPref("services.sync.engine.addons"))
michael@0 2137 return "addons-sync-disabled";
michael@0 2138 }
michael@0 2139 return null;
michael@0 2140 ]]></getter>
michael@0 2141 </property>
michael@0 2142 <property name="_notificationMessage">
michael@0 2143 <getter><![CDATA[
michael@0 2144 return gNavigatorBundle.getFormattedString(
michael@0 2145 "syncPromoNotification." + this._notificationType + ".description",
michael@0 2146 [this._brandBundle.GetStringFromName("syncBrandShortName")]
michael@0 2147 );
michael@0 2148 ]]></getter>
michael@0 2149 </property>
michael@0 2150 <property name="_notificationLink">
michael@0 2151 <getter><![CDATA[
michael@0 2152 if (this._notificationType == "addons-sync-disabled") {
michael@0 2153 return "https://support.mozilla.org/kb/how-do-i-enable-add-sync";
michael@0 2154 }
michael@0 2155 return "https://services.mozilla.com/sync/";
michael@0 2156 ]]></getter>
michael@0 2157 </property>
michael@0 2158 <method name="onCloseButtonCommand">
michael@0 2159 <body><![CDATA[
michael@0 2160 this._viewsLeft = 0;
michael@0 2161 this.hidden = true;
michael@0 2162 ]]></body>
michael@0 2163 </method>
michael@0 2164 <method name="onLinkClick">
michael@0 2165 <body><![CDATA[
michael@0 2166 // Open a new selected tab and close the current panel.
michael@0 2167 openUILinkIn(this._promolink.getAttribute("href"), "tab");
michael@0 2168 this._panel.hidePopup();
michael@0 2169 ]]></body>
michael@0 2170 </method>
michael@0 2171 <method name="handleEvent">
michael@0 2172 <parameter name="event"/>
michael@0 2173 <body><![CDATA[
michael@0 2174 if (event.type != "popupshowing" || event.target != this._panel)
michael@0 2175 return;
michael@0 2176
michael@0 2177 // A previous notification may have unhidden this.
michael@0 2178 this.hidden = true;
michael@0 2179
michael@0 2180 // Only handle supported notification panels.
michael@0 2181 if (!this._notificationType) {
michael@0 2182 return;
michael@0 2183 }
michael@0 2184
michael@0 2185 let viewsLeft = this._viewsLeft;
michael@0 2186 if (viewsLeft) {
michael@0 2187 if (Services.prefs.prefHasUserValue("services.sync.username") &&
michael@0 2188 this._notificationType != "addons-sync-disabled") {
michael@0 2189 // If the user has already setup Sync, don't show the notification.
michael@0 2190 this._viewsLeft = 0;
michael@0 2191 // Be sure to hide the panel, in case it was visible and the user
michael@0 2192 // decided to setup Sync after noticing it.
michael@0 2193 viewsLeft = 0;
michael@0 2194 // The panel is still hidden, just bail out.
michael@0 2195 return;
michael@0 2196 }
michael@0 2197 else {
michael@0 2198 this._viewsLeft = viewsLeft - 1;
michael@0 2199 }
michael@0 2200
michael@0 2201 this._promolink.setAttribute("href", this._notificationLink);
michael@0 2202 this._promolink.value = gNavigatorBundle.getString("syncPromoNotification.learnMoreLinkText");
michael@0 2203
michael@0 2204 this.hidden = false;
michael@0 2205
michael@0 2206 // HACK: The description element doesn't wrap correctly in panels,
michael@0 2207 // thus set a width on it, based on the available space, before
michael@0 2208 // setting its textContent. Then set its height as well, to
michael@0 2209 // fix wrong height calculation on Linux (bug 659578).
michael@0 2210 this._panel.addEventListener("popupshown", function panelShown() {
michael@0 2211 this._panel.removeEventListener("popupshown", panelShown, true);
michael@0 2212 // Previous popupShown events may close the panel or change
michael@0 2213 // its contents, so ensure this is still valid.
michael@0 2214 if (this._panel.state != "open" || !this._notificationType)
michael@0 2215 return;
michael@0 2216 this._promomessage.width = this._promomessage.getBoundingClientRect().width;
michael@0 2217 this._promomessage.firstChild.textContent = this._notificationMessage;
michael@0 2218 this._promomessage.height = this._promomessage.getBoundingClientRect().height;
michael@0 2219 }.bind(this), true);
michael@0 2220 }
michael@0 2221 ]]></body>
michael@0 2222 </method>
michael@0 2223 </implementation>
michael@0 2224 </binding>
michael@0 2225
michael@0 2226 <binding id="toolbarbutton-badged" display="xul:button"
michael@0 2227 extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
michael@0 2228 <content>
michael@0 2229 <children includes="observes|template|menupopup|panel|tooltip"/>
michael@0 2230 <xul:hbox class="toolbarbutton-badge-container" align="start" pack="end">
michael@0 2231 <xul:hbox class="toolbarbutton-badge" xbl:inherits="badge"/>
michael@0 2232 <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
michael@0 2233 </xul:hbox>
michael@0 2234 <xul:label class="toolbarbutton-text" crop="right" flex="1"
michael@0 2235 xbl:inherits="value=label,accesskey,crop,wrap"/>
michael@0 2236 <xul:label class="toolbarbutton-multiline-text" flex="1"
michael@0 2237 xbl:inherits="xbl:text=label,accesskey,wrap"/>
michael@0 2238 </content>
michael@0 2239 </binding>
michael@0 2240
michael@0 2241 </bindings>

mercurial