browser/base/content/urlbarBindings.xml

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

mercurial