4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
11 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
15 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
16 xmlns:html="http://www.w3.org/1999/xhtml"
17 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
18 xmlns:xbl="http://www.mozilla.org/xbl">
20 <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
22 <content sizetopopup="pref">
23 <xul:hbox anonid="textbox-container"
24 class="autocomplete-textbox-container urlbar-textbox-container"
25 flex="1" xbl:inherits="focused">
26 <children includes="image|deck|stack|box">
27 <xul:image class="autocomplete-icon" allowevents="true"/>
29 <xul:hbox anonid="textbox-input-box"
30 class="textbox-input-box urlbar-input-box"
31 flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
33 <html:input anonid="input"
34 class="autocomplete-textbox urlbar-input textbox-input uri-element-right-align"
36 xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
38 <children includes="hbox"/>
40 <xul:dropmarker anonid="historydropmarker"
41 class="autocomplete-history-dropmarker urlbar-history-dropmarker"
43 xbl:inherits="open,enablehistory,parentfocused=focused"/>
44 <xul:popupset anonid="popupset"
45 class="autocomplete-result-popupset"/>
46 <children includes="toolbarbutton"/>
49 <implementation implements="nsIObserver, nsIDOMEventListener">
50 <constructor><![CDATA[
51 this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
52 .getService(Components.interfaces.nsIPrefService)
53 .getBranch("browser.urlbar.");
55 this._prefs.addObserver("", this, false);
56 this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
57 this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
58 this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
59 this.timeout = this._prefs.getIntPref("delay");
60 this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled");
61 this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
62 this._ignoreNextSelect = false;
64 this.inputField.controllers.insertControllerAt(0, this._copyCutController);
65 this.inputField.addEventListener("mousedown", this, false);
66 this.inputField.addEventListener("mousemove", this, false);
67 this.inputField.addEventListener("mouseout", this, false);
68 this.inputField.addEventListener("overflow", this, false);
69 this.inputField.addEventListener("underflow", this, false);
72 if (this._prefs.getBoolPref("unifiedcomplete")) {
73 this.setAttribute("autocompletesearch", "unifiedcomplete");
77 const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
78 var textBox = document.getAnonymousElementByAttribute(this,
79 "anonid", "textbox-input-box");
80 var cxmenu = document.getAnonymousElementByAttribute(textBox,
81 "anonid", "input-box-contextmenu");
83 cxmenu.addEventListener("popupshowing", function() {
86 var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
87 var enabled = controller.isCommandEnabled("cmd_paste");
89 pasteAndGo.removeAttribute("disabled");
91 pasteAndGo.setAttribute("disabled", "true");
94 var insertLocation = cxmenu.firstChild;
95 while (insertLocation.nextSibling &&
96 insertLocation.getAttribute("cmd") != "cmd_paste")
97 insertLocation = insertLocation.nextSibling;
99 pasteAndGo = document.createElement("menuitem");
100 let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
101 GetStringFromName("pasteAndGo.label");
102 pasteAndGo.setAttribute("label", label);
103 pasteAndGo.setAttribute("anonid", "paste-and-go");
104 pasteAndGo.setAttribute("oncommand",
105 "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();");
106 cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
110 <destructor><![CDATA[
111 this._prefs.removeObserver("", this);
113 this.inputField.controllers.removeController(this._copyCutController);
114 this.inputField.removeEventListener("mousedown", this, false);
115 this.inputField.removeEventListener("mousemove", this, false);
116 this.inputField.removeEventListener("mouseout", this, false);
117 this.inputField.removeEventListener("overflow", this, false);
118 this.inputField.removeEventListener("underflow", this, false);
121 <field name="_value"></field>
124 onBeforeValueGet is called by the base-binding's .value getter.
125 It can return an object with a "value" property, to override the
126 return value of the getter.
128 <method name="onBeforeValueGet">
130 if (this.hasAttribute("actiontype"))
131 return {value: this._value};
137 onBeforeValueSet is called by the base-binding's .value setter.
138 It should return the value that the setter should use.
140 <method name="onBeforeValueSet">
141 <parameter name="aValue"/>
143 this._value = aValue;
144 var returnValue = aValue;
145 var action = this._parseActionUrl(aValue);
146 // Don't put back the action if we are invoked while override actions
148 if (action && this._numNoActionsKeys <= 0) {
149 returnValue = action.param;
150 this.setAttribute("actiontype", action.type);
152 this.removeAttribute("actiontype");
158 <field name="_mayTrimURLs">true</field>
159 <method name="trimValue">
160 <parameter name="aURL"/>
162 // This method must not modify the given URL such that calling
163 // nsIURIFixup::createFixupURI with the result will produce a different URI.
164 return this._mayTrimURLs ? trimURL(aURL) : aURL;
168 <field name="_formattingEnabled">true</field>
169 <method name="formatValue">
171 if (!this._formattingEnabled || this.focused)
174 let controller = this.editor.selectionController;
175 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
176 selection.removeAllRanges();
178 let textNode = this.editor.rootElement.firstChild;
179 let value = textNode.textContent;
181 let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
183 ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
185 let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
189 let [, preDomain, domain] = matchedURL;
190 let baseDomain = domain;
192 // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
193 if (domain[0] != "[") {
195 baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
196 if (!domain.endsWith(baseDomain)) {
197 // getBaseDomainFromHost converts its resultant to ACE.
198 let IDNService = Cc["@mozilla.org/network/idn-service;1"]
199 .getService(Ci.nsIIDNService);
200 baseDomain = IDNService.convertACEtoUTF8(baseDomain);
204 if (baseDomain != domain) {
205 subDomain = domain.slice(0, -baseDomain.length);
208 let rangeLength = preDomain.length + subDomain.length;
210 let range = document.createRange();
211 range.setStart(textNode, 0);
212 range.setEnd(textNode, rangeLength);
213 selection.addRange(range);
216 let startRest = preDomain.length + domain.length;
217 if (startRest < value.length) {
218 let range = document.createRange();
219 range.setStart(textNode, startRest);
220 range.setEnd(textNode, value.length);
221 selection.addRange(range);
226 <method name="_clearFormatting">
228 if (!this._formattingEnabled)
231 let controller = this.editor.selectionController;
232 let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
233 selection.removeAllRanges();
237 <method name="handleRevert">
239 var isScrolling = this.popupOpen;
241 gBrowser.userTypedValue = null;
243 // don't revert to last valid url unless page is NOT loading
244 // and user is NOT key-scrolling through autocomplete list
245 if (!XULBrowserWindow.isBusy && !isScrolling) {
248 // If the value isn't empty and the urlbar has focus, select the value.
249 if (this.value && this.hasAttribute("focused"))
253 // tell widget to revert to last typed text only if the user
254 // was scrolling when they hit escape
259 <method name="handleCommand">
260 <parameter name="aTriggeringEvent"/>
262 if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2)
263 return; // Do nothing for right clicks
265 var url = this.value;
266 var mayInheritPrincipal = false;
269 var action = this._parseActionUrl(url);
270 let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
272 let matchLastLocationChange = true;
275 if (this.hasAttribute("actiontype")) {
276 if (action.type == "switchtab") {
278 let prevTab = gBrowser.selectedTab;
279 if (switchToTabHavingURI(url) &&
281 gBrowser.removeTab(prevTab);
285 continueOperation.call(this);
288 this._canonizeURL(aTriggeringEvent, response => {
289 [url, postData, mayInheritPrincipal] = response;
291 matchLastLocationChange = (lastLocationChange ==
292 gBrowser.selectedBrowser.lastLocationChange);
293 continueOperation.call(this);
298 function continueOperation()
301 gBrowser.userTypedValue = url;
303 addToUrlbarHistory(url);
305 // Things may go wrong when adding url to session history,
306 // but don't let that interfere with the loading of the url.
310 function loadCurrent() {
311 let webnav = Ci.nsIWebNavigation;
312 let flags = webnav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
313 webnav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
314 // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
315 // inheriting the currently loaded document's principal, unless this
316 // URL is marked as safe to inherit (e.g. came from a bookmark
318 if (!mayInheritPrincipal)
319 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
320 gBrowser.loadURIWithFlags(url, flags, null, null, postData);
323 // Focus the content area before triggering loads, since if the load
324 // occurs in a new tab, we want focus to be restored to the content
325 // area when the current tab is re-selected.
326 gBrowser.selectedBrowser.focus();
328 let isMouseEvent = aTriggeringEvent instanceof MouseEvent;
329 let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey;
332 // XXX This was added a long time ago, and I'm not sure why it is
333 // necessary. Alt+Enter's default action might cause a system beep,
334 // or something like that?
335 aTriggeringEvent.preventDefault();
336 aTriggeringEvent.stopPropagation();
339 // If the current tab is empty, ignore Alt+Enter (just reuse this tab)
340 altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab);
342 if (isMouseEvent || altEnter) {
343 // Use the standard UI link behaviors for clicks or Alt+Enter
346 where = whereToOpenLink(aTriggeringEvent, false, false);
348 if (where == "current") {
349 if (matchLastLocationChange) {
354 let params = { allowThirdPartyFixup: true,
356 initiatingDoc: document };
357 openUILinkIn(url, where, params);
360 if (matchLastLocationChange) {
368 <method name="_canonizeURL">
369 <parameter name="aTriggeringEvent"/>
370 <parameter name="aCallback"/>
372 var url = this.value;
374 aCallback(["", null, false]);
378 // Only add the suffix when the URL bar value isn't already "URL-like",
379 // and only if we get a keyboard event, to match user expectations.
380 if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(url) &&
381 (aTriggeringEvent instanceof KeyEvent)) {
383 let accel = aTriggeringEvent.metaKey;
385 let accel = aTriggeringEvent.ctrlKey;
387 let shift = aTriggeringEvent.shiftKey;
392 case (accel && shift):
400 suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
401 if (suffix.charAt(suffix.length - 1) != "/")
410 // trim leading/trailing spaces (bug 233205)
413 // Tack www. and suffix on. If user has appended directories, insert
414 // suffix before them (bug 279035). Be careful not to get two slashes.
416 let firstSlash = url.indexOf("/");
418 if (firstSlash >= 0) {
419 url = url.substring(0, firstSlash) + suffix +
420 url.substring(firstSlash + 1);
425 url = "http://www." + url;
429 getShortcutOrURIAndPostData(url, data => {
430 aCallback([data.url, data.postData, data.mayInheritPrincipal]);
435 <field name="_contentIsCropped">false</field>
437 <method name="_initURLTooltip">
439 if (this.focused || !this._contentIsCropped)
441 this.inputField.setAttribute("tooltiptext", this.value);
445 <method name="_hideURLTooltip">
447 this.inputField.removeAttribute("tooltiptext");
451 <method name="onDragOver">
452 <parameter name="aEvent"/>
454 var types = aEvent.dataTransfer.types;
455 if (types.contains("application/x-moz-file") ||
456 types.contains("text/x-moz-url") ||
457 types.contains("text/uri-list") ||
458 types.contains("text/unicode"))
459 aEvent.preventDefault();
463 <method name="onDrop">
464 <parameter name="aEvent"/>
466 let url = browserDragAndDrop.drop(aEvent, { })
468 // The URL bar automatically handles inputs with newline characters,
469 // so we can get away with treating text/x-moz-url flavours as text/plain.
471 aEvent.preventDefault();
473 SetPageProxyState("invalid");
476 urlSecurityCheck(url,
477 gBrowser.contentPrincipal,
478 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
482 this.handleCommand();
487 <method name="_getSelectedValueForClipboard">
489 // Grab the actual input field's value, not our value, which could include moz-action:
490 var inputVal = this.inputField.value;
491 var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd);
493 // If the selection doesn't start at the beginning or doesn't span the full domain or
494 // the URL bar is modified, nothing else to do here.
495 if (this.selectionStart > 0 || this.valueIsTyped)
497 // The selection doesn't span the full domain if it doesn't contain a slash and is
498 // followed by some character other than a slash.
499 if (!selectedVal.contains("/")) {
500 let remainder = inputVal.replace(selectedVal, "");
501 if (remainder != "" && remainder[0] != "/")
505 let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
509 uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
514 // Only copy exposable URIs
516 uri = uriFixup.createExposableURI(uri);
519 // If the entire URL is selected, just use the actual loaded URI.
520 if (inputVal == selectedVal) {
521 // ... but only if isn't a javascript: or data: URI, since those
522 // are hard to read when encoded
523 if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
524 // Parentheses are known to confuse third-party applications (bug 458565).
525 selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
531 // Just the beginning of the URL is selected, check for a trimmed
534 let trimmedSpec = this.trimValue(spec);
535 if (spec != trimmedSpec) {
536 // Prepend the portion that trimValue removed from the beginning.
537 // This assumes trimValue will only truncate the URL at
538 // the beginning or end (or both).
539 let trimmedSegments = spec.split(trimmedSpec);
540 selectedVal = trimmedSegments[0] + selectedVal;
547 <field name="_copyCutController"><![CDATA[
550 doCommand: function(aCommand) {
551 var urlbar = this.urlbar;
552 var val = urlbar._getSelectedValueForClipboard();
556 if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
557 let start = urlbar.selectionStart;
558 let end = urlbar.selectionEnd;
559 urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
560 urlbar.inputField.value.substring(end);
561 urlbar.selectionStart = urlbar.selectionEnd = start;
562 urlbar.removeAttribute("actiontype");
563 SetPageProxyState("invalid");
566 Cc["@mozilla.org/widget/clipboardhelper;1"]
567 .getService(Ci.nsIClipboardHelper)
568 .copyString(val, document);
570 supportsCommand: function(aCommand) {
578 isCommandEnabled: function(aCommand) {
579 return this.supportsCommand(aCommand) &&
580 (aCommand != "cmd_cut" || !this.urlbar.readOnly) &&
581 this.urlbar.selectionStart < this.urlbar.selectionEnd;
583 onEvent: function(aEventName) {}
587 <method name="observe">
588 <parameter name="aSubject"/>
589 <parameter name="aTopic"/>
590 <parameter name="aData"/>
592 if (aTopic == "nsPref:changed") {
594 case "clickSelectsAll":
595 case "doubleClickSelectsAll":
596 this[aData] = this._prefs.getBoolPref(aData);
599 this.completeDefaultIndex = this._prefs.getBoolPref(aData);
602 this.timeout = this._prefs.getIntPref(aData);
604 case "formatting.enabled":
605 this._formattingEnabled = this._prefs.getBoolPref(aData);
608 this._mayTrimURLs = this._prefs.getBoolPref(aData);
610 case "unifiedcomplete":
611 let useUnifiedComplete = false;
613 useUnifiedComplete = this._prefs.getBoolPref(aData);
615 this.setAttribute("autocompletesearch",
616 useUnifiedComplete ? "unifiedcomplete"
617 : "urlinline history");
623 <method name="handleEvent">
624 <parameter name="aEvent"/>
626 switch (aEvent.type) {
628 if (this.doubleClickSelectsAll &&
629 aEvent.button == 0 && aEvent.detail == 2) {
630 this.editor.selectAll();
631 aEvent.preventDefault();
635 this._initURLTooltip();
638 this._hideURLTooltip();
641 this._contentIsCropped = true;
644 this._contentIsCropped = false;
645 this._hideURLTooltip();
651 <property name="textValue"
652 onget="return this.value;">
656 val = losslessDecodeURI(makeURI(val));
659 // Trim popup selected values, but never trim results coming from
661 if (this.popup.selectedIndex == -1)
662 this._disableTrim = true;
664 this._disableTrim = false;
666 // Completing a result should simulate the user typing the result, so
667 // fire an input event.
668 let evt = document.createEvent("UIEvents");
669 evt.initUIEvent("input", true, false, window, 0);
670 this.mIgnoreInput = true;
671 this.dispatchEvent(evt);
672 this.mIgnoreInput = false;
679 <method name="_parseActionUrl">
680 <parameter name="aUrl"/>
682 if (!aUrl.startsWith("moz-action:"))
685 // url is in the format moz-action:ACTION,PARAM
686 let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
687 return {type: action, param: param};
691 <field name="_numNoActionsKeys"><![CDATA[
695 <method name="_clearNoActions">
696 <parameter name="aURL"/>
698 this._numNoActionsKeys = 0;
699 this.popup.removeAttribute("noactions");
700 let action = this._parseActionUrl(this._value);
702 this.setAttribute("actiontype", action.type);
706 <method name="selectTextRange">
707 <parameter name="aStartIndex"/>
708 <parameter name="aEndIndex"/>
710 this._ignoreNextSelect = true;
711 this.inputField.setSelectionRange(aStartIndex, aEndIndex);
717 <handler event="keydown"><![CDATA[
718 if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
719 event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
720 this.popup.selectedIndex >= 0) {
721 this._numNoActionsKeys++;
722 this.popup.setAttribute("noactions", "true");
723 this.removeAttribute("actiontype");
727 <handler event="keyup"><![CDATA[
728 if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
729 event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
730 this._numNoActionsKeys > 0) {
731 this._numNoActionsKeys--;
732 if (this._numNoActionsKeys == 0)
733 this._clearNoActions();
737 <handler event="blur"><![CDATA[
738 this._clearNoActions();
742 <handler event="dragstart" phase="capturing"><![CDATA[
743 // Drag only if the gesture starts from the input field.
744 if (event.originalTarget != this.inputField)
747 // Drag only if the entire value is selected and it's a valid URI.
748 var isFullSelection = this.selectionStart == 0 &&
749 this.selectionEnd == this.textLength;
750 if (!isFullSelection ||
751 this.getAttribute("pageproxystate") != "valid")
754 var urlString = content.location.href;
755 var title = content.document.title || urlString;
756 var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>";
758 var dt = event.dataTransfer;
759 dt.setData("text/x-moz-url", urlString + "\n" + title);
760 dt.setData("text/unicode", urlString);
761 dt.setData("text/html", htmlString);
763 dt.effectAllowed = "copyLink";
764 event.stopPropagation();
767 <handler event="focus" phase="capturing"><![CDATA[
768 this._hideURLTooltip();
769 this._clearFormatting();
772 <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
773 <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
774 <handler event="select"><![CDATA[
775 if (this._ignoreNextSelect) {
776 // If this select event is coming from autocomplete's selectTextRange,
777 // then we don't need to adjust what's on the selection keyboard here,
778 // but make sure to reset the flag since this should be a one-time
780 this._ignoreNextSelect = false;
784 if (!Cc["@mozilla.org/widget/clipboard;1"]
785 .getService(Ci.nsIClipboard)
786 .supportsSelectionClipboard())
789 var val = this._getSelectedValueForClipboard();
793 Cc["@mozilla.org/widget/clipboardhelper;1"]
794 .getService(Ci.nsIClipboardHelper)
795 .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard, document);
801 <!-- Note: this binding is applied to the autocomplete popup used in the Search bar and in web page content -->
802 <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup">
804 <method name="openAutocompletePopup">
805 <parameter name="aInput"/>
806 <parameter name="aElement"/>
809 // initially the panel is hidden
810 // to avoid impacting startup / new window performance
811 aInput.popup.hidden = false;
813 // this method is defined on the base binding
814 this._openAutocompletePopup(aInput, aElement);
818 <method name="onPopupClick">
819 <parameter name="aEvent"/>
821 // Ignore all right-clicks
822 if (aEvent.button == 2)
825 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
827 // Check for unmodified left-click, and use default behavior
828 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
829 !aEvent.altKey && !aEvent.metaKey) {
830 controller.handleEnter(true);
834 // Check for middle-click or modified clicks on the search bar
835 var searchBar = BrowserSearch.searchBar;
836 if (searchBar && searchBar.textbox == this.mInput) {
837 // Handle search bar popup clicks
838 var search = controller.getValueAt(this.selectedIndex);
840 // close the autocomplete popup and revert the entered search term
842 controller.handleEscape();
844 // Fill in the search bar's value
845 searchBar.value = search;
847 // open the search results according to the clicking subtlety
848 var where = whereToOpenLink(aEvent, false, true);
849 searchBar.doSearch(search, where);
856 <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
858 <field name="_maxResults">0</field>
860 <field name="_bundle" readonly="true">
861 Cc["@mozilla.org/intl/stringbundle;1"].
862 getService(Ci.nsIStringBundleService).
863 createBundle("chrome://browser/locale/places/places.properties");
866 <property name="maxResults" readonly="true">
869 if (!this._maxResults) {
871 Components.classes["@mozilla.org/preferences-service;1"]
872 .getService(Components.interfaces.nsIPrefBranch);
873 this._maxResults = prefService.getIntPref("browser.urlbar.maxRichResults");
875 return this._maxResults;
880 <method name="openAutocompletePopup">
881 <parameter name="aInput"/>
882 <parameter name="aElement"/>
885 // initially the panel is hidden
886 // to avoid impacting startup / new window performance
887 aInput.popup.hidden = false;
889 // this method is defined on the base binding
890 this._openAutocompletePopup(aInput, aElement);
894 <method name="onPopupClick">
895 <parameter name="aEvent"/>
898 // Ignore right-clicks
899 if (aEvent.button == 2)
902 var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
904 // Check for unmodified left-click, and use default behavior
905 if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
906 !aEvent.altKey && !aEvent.metaKey) {
907 controller.handleEnter(true);
911 // Check for middle-click or modified clicks on the URL bar
912 if (gURLBar && this.mInput == gURLBar) {
913 var url = controller.getValueAt(this.selectedIndex);
915 // close the autocomplete popup and revert the entered address
917 controller.handleEscape();
919 // Check if this is meant to be an action
920 let action = this.mInput._parseActionUrl(url);
922 if (action.type == "switchtab")
928 // respect the usual clicking subtleties
929 openUILink(url, aEvent);
935 <method name="createResultLabel">
936 <parameter name="aTitle"/>
937 <parameter name="aUrl"/>
938 <parameter name="aType"/>
941 var label = aTitle + " " + aUrl;
942 // convert aType (ex: "ac-result-type-<aType>") to text to be spoke aloud
943 // by screen readers. convert "tag" and "bookmark" to the localized versions,
944 // but don't do anything for "favicon" (the default)
945 if (aType != "favicon") {
946 label += " " + this._bundle.GetStringFromName(aType + "ResultLabel");
956 <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
957 <content align="start">
958 <xul:image class="popup-notification-icon"
959 xbl:inherits="popupid,src=icon"/>
961 <xul:description class="popup-notification-description addon-progress-description"
962 xbl:inherits="xbl:text=label"/>
963 <xul:spacer flex="1"/>
964 <xul:hbox align="center">
965 <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/>
966 <xul:button anonid="cancel" class="popup-progress-cancel" oncommand="document.getBindingParent(this).cancel()"/>
968 <xul:label anonid="progresstext" class="popup-progress-label"/>
969 <xul:hbox class="popup-notification-button-container"
970 pack="end" align="center">
971 <xul:button anonid="button"
972 class="popup-notification-menubutton"
974 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
975 <xul:menupopup anonid="menupopup"
976 xbl:inherits="oncommand=menucommand">
978 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
979 label="&closeNotificationItem.label;"
980 xbl:inherits="oncommand=closeitemcommand"/>
985 <xul:vbox pack="start">
986 <xul:toolbarbutton anonid="closebutton"
987 class="messageCloseButton close-icon popup-notification-closebutton tabbable"
988 xbl:inherits="oncommand=closebuttoncommand"
989 tooltiptext="&closeNotification.tooltip;"/>
993 <constructor><![CDATA[
994 this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip"));
996 this.notification.options.installs.forEach(function(aInstall) {
997 aInstall.addListener(this);
1000 // Calling updateProgress can sometimes cause this notification to be
1001 // removed in the middle of refreshing the notification panel which
1002 // makes the panel get refreshed again. Just initialise to the
1003 // undetermined state and then schedule a proper check at the next
1005 this.setProgress(0, -1);
1006 this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0);
1009 <destructor><![CDATA[
1013 <field name="progressmeter" readonly="true">
1014 document.getAnonymousElementByAttribute(this, "anonid", "progressmeter");
1016 <field name="progresstext" readonly="true">
1017 document.getAnonymousElementByAttribute(this, "anonid", "progresstext");
1019 <field name="cancelbtn" readonly="true">
1020 document.getAnonymousElementByAttribute(this, "anonid", "cancel");
1022 <field name="DownloadUtils" readonly="true">
1024 Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils);
1025 utils.DownloadUtils;
1028 <method name="destroy">
1030 this.notification.options.installs.forEach(function(aInstall) {
1031 aInstall.removeListener(this);
1033 clearTimeout(this._updateProgressTimeout);
1037 <method name="setProgress">
1038 <parameter name="aProgress"/>
1039 <parameter name="aMaxProgress"/>
1041 if (aMaxProgress == -1) {
1042 this.progressmeter.mode = "undetermined";
1045 this.progressmeter.mode = "determined";
1046 this.progressmeter.value = (aProgress * 100) / aMaxProgress;
1049 let now = Date.now();
1051 if (!this.notification.lastUpdate) {
1052 this.notification.lastUpdate = now;
1053 this.notification.lastProgress = aProgress;
1057 let delta = now - this.notification.lastUpdate;
1058 if ((delta < 400) && (aProgress < aMaxProgress))
1063 // This code is taken from nsDownloadManager.cpp
1064 let speed = (aProgress - this.notification.lastProgress) / delta;
1065 if (this.notification.speed)
1066 speed = speed * 0.9 + this.notification.speed * 0.1;
1068 this.notification.lastUpdate = now;
1069 this.notification.lastProgress = aProgress;
1070 this.notification.speed = speed;
1073 [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last);
1074 this.progresstext.value = status;
1078 <method name="cancel">
1080 // Cache these as cancelling the installs will remove this
1081 // notification which will drop these references
1082 let browser = this.notification.browser;
1083 let contentWindow = this.notification.options.contentWindow;
1084 let sourceURI = this.notification.options.sourceURI;
1086 let installs = this.notification.options.installs;
1087 installs.forEach(function(aInstall) {
1092 // Cancel will throw if the download has already failed
1096 let anchorID = "addons-notification-icon";
1097 let notificationID = "addon-install-cancelled";
1098 let messageString = gNavigatorBundle.getString("addonDownloadCancelled");
1099 messageString = PluralForm.get(installs.length, messageString);
1100 let buttonText = gNavigatorBundle.getString("addonDownloadRestart");
1101 buttonText = PluralForm.get(installs.length, buttonText);
1105 accessKey: gNavigatorBundle.getString("addonDownloadRestart.accessKey"),
1106 callback: function() {
1107 let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
1108 getService(Ci.amIWebInstallListener);
1109 if (weblistener.onWebInstallRequested(contentWindow, sourceURI,
1110 installs, installs.length)) {
1111 installs.forEach(function(aInstall) {
1118 PopupNotifications.show(browser, notificationID, messageString,
1123 <method name="updateProgress">
1125 let downloadingCount = 0;
1127 let maxProgress = 0;
1129 this.notification.options.installs.forEach(function(aInstall) {
1130 if (aInstall.maxProgress == -1)
1132 progress += aInstall.progress;
1133 if (maxProgress >= 0)
1134 maxProgress += aInstall.maxProgress;
1135 if (aInstall.state < AddonManager.STATE_DOWNLOADED)
1139 if (downloadingCount == 0) {
1141 PopupNotifications.remove(this.notification);
1144 this.setProgress(progress, maxProgress);
1149 <method name="onDownloadProgress">
1151 this.updateProgress();
1155 <method name="onDownloadFailed">
1157 this.updateProgress();
1161 <method name="onDownloadCancelled">
1163 this.updateProgress();
1167 <method name="onDownloadEnded">
1169 this.updateProgress();
1175 <binding id="identity-request-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
1176 <content align="start">
1178 <xul:image class="popup-notification-icon"
1179 xbl:inherits="popupid,src=icon"/>
1182 <xul:vbox anonid="identity-deck">
1183 <xul:vbox flex="1" pack="center"> <!-- 1: add an email -->
1184 <html:input type="email" anonid="email" required="required" size="30"/>
1185 <xul:description anonid="newidentitydesc"/>
1186 <xul:spacer flex="1"/>
1187 <xul:label class="text-link custom-link small-margin" anonid="chooseemail" hidden="true"/>
1189 <xul:vbox flex="1" hidden="true"> <!-- 2: choose an email -->
1190 <xul:description anonid="chooseidentitydesc"/>
1191 <xul:radiogroup anonid="identities">
1193 <xul:label class="text-link custom-link" anonid="newemail"/>
1196 <xul:hbox class="popup-notification-button-container"
1197 pack="end" align="center">
1198 <xul:label anonid="tos" class="text-link" hidden="true"/>
1199 <xul:label anonid="privacypolicy" class="text-link" hidden="true"/>
1200 <xul:spacer flex="1"/>
1201 <xul:image anonid="throbber" src="chrome://browser/skin/tabbrowser/loading.png"
1202 style="visibility:hidden" width="16" height="16"/>
1203 <xul:button anonid="button"
1205 class="popup-notification-menubutton"
1206 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
1207 <xul:menupopup anonid="menupopup"
1208 xbl:inherits="oncommand=menucommand">
1210 <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
1211 label="&closeNotificationItem.label;"
1212 xbl:inherits="oncommand=closeitemcommand"/>
1217 <xul:vbox pack="start">
1218 <xul:toolbarbutton anonid="closebutton"
1219 class="messageCloseButton close-icon popup-notification-closebutton tabbable"
1220 xbl:inherits="oncommand=closebuttoncommand"
1221 tooltiptext="&closeNotification.tooltip;"/>
1225 <constructor><![CDATA[
1226 // this.notification.options.identity is used to pass identity-specific info to the binding
1227 let origin = this.identity.origin
1230 this.emailField.placeholder = gNavigatorBundle.
1231 getString("identity.newIdentity.email.placeholder");
1232 this.newIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
1233 "identity.newIdentity.description", [origin]);
1234 this.chooseIdentityDesc.textContent = gNavigatorBundle.getFormattedString(
1235 "identity.chooseIdentity.description", [origin]);
1237 // Show optional terms of service and privacy policy links
1238 this._populateLink(this.identity.termsOfService, "tos", "identity.termsOfService");
1239 this._populateLink(this.identity.privacyPolicy, "privacypolicy", "identity.privacyPolicy");
1241 // Populate the list of identities to choose from. The origin is used to provide
1242 // better suggestions.
1243 let identities = this.SignInToWebsiteUX.getIdentitiesForSite(origin);
1245 this._populateIdentityList(identities);
1247 if (typeof this.step == "undefined") {
1248 // First opening of this notification
1249 // Show the add email pane (0) if there are no existing identities otherwise show the list
1250 this.step = "result" in identities && identities.result.length ? 1 : 0;
1252 // Already opened so restore previous state
1253 if (this.identity.typedEmail) {
1254 this.emailField.value = this.identity.typedEmail;
1256 if (this.identity.selected) {
1257 // If the user already chose an identity then update the UI to reflect that
1258 this.onIdentitySelected();
1260 // Update the view for the step
1261 this.step = this.step;
1264 // Fire notification with the chosen identity when main button is clicked
1265 this.button.addEventListener("command", this._onButtonCommand.bind(this), true);
1267 // Do the same if enter is pressed in the email field
1268 this.emailField.addEventListener("keypress", function emailFieldKeypress(aEvent) {
1269 if (aEvent.keyCode != aEvent.DOM_VK_RETURN)
1271 this._onButtonCommand(aEvent);
1274 this.addEmailLink.value = gNavigatorBundle.getString("identity.newIdentity.label");
1275 this.addEmailLink.accessKey = gNavigatorBundle.getString("identity.newIdentity.accessKey");
1276 this.addEmailLink.addEventListener("click", function addEmailClick(evt) {
1280 this.chooseEmailLink.value = gNavigatorBundle.getString("identity.chooseIdentity.label");
1281 this.chooseEmailLink.hidden = !("result" in identities && identities.result.length);
1282 this.chooseEmailLink.addEventListener("click", function chooseEmailClick(evt) {
1286 this.emailField.addEventListener("blur", function onEmailBlur() {
1287 this.identity.typedEmail = this.emailField.value;
1291 <field name="SignInToWebsiteUX" readonly="true">
1293 Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw);
1294 sitw.SignInToWebsiteUX;
1297 <field name="newIdentityDesc" readonly="true">
1298 document.getAnonymousElementByAttribute(this, "anonid", "newidentitydesc");
1301 <field name="chooseIdentityDesc" readonly="true">
1302 document.getAnonymousElementByAttribute(this, "anonid", "chooseidentitydesc");
1305 <field name="identityList" readonly="true">
1306 document.getAnonymousElementByAttribute(this, "anonid", "identities");
1309 <field name="emailField" readonly="true">
1310 document.getAnonymousElementByAttribute(this, "anonid", "email");
1313 <field name="addEmailLink" readonly="true">
1314 document.getAnonymousElementByAttribute(this, "anonid", "newemail");
1317 <field name="chooseEmailLink" readonly="true">
1318 document.getAnonymousElementByAttribute(this, "anonid", "chooseemail");
1321 <field name="throbber" readonly="true">
1322 document.getAnonymousElementByAttribute(this, "anonid", "throbber");
1325 <field name="identity" readonly="true">
1326 this.notification.options.identity;
1329 <!-- persist the state on the identity object so we can re-create the
1330 notification state upon re-opening -->
1331 <property name="step">
1333 return this.identity.step;
1336 let deck = document.getAnonymousElementByAttribute(this, "anonid", "identity-deck");
1337 for (let i = 0; i < deck.children.length; i++) {
1338 deck.children[i].hidden = (val != i);
1340 this.identity.step = val;
1343 this.emailField.focus();
1349 <method name="onIdentitySelected">
1351 this.throbber.style.visibility = "visible";
1352 this.button.disabled = true;
1353 this.emailField.value = this.identity.selected
1354 this.emailField.disabled = true;
1355 this.identityList.disabled = true;
1359 <method name="_populateLink">
1360 <parameter name="aURL"/>
1361 <parameter name="aLinkId"/>
1362 <parameter name="aStringId"/>
1365 // Show optional link to aURL
1366 let link = document.getAnonymousElementByAttribute(this, "anonid", aLinkId);
1367 link.value = gNavigatorBundle.getString(aStringId);
1369 link.hidden = false;
1374 <method name="_populateIdentityList">
1375 <parameter name="aIdentities"/>
1377 let foundLastUsed = false;
1378 let lastUsed = this.identity.selected || aIdentities.lastUsed;
1379 for (let id in aIdentities.result) {
1380 let label = aIdentities.result[id];
1381 let opt = this.identityList.appendItem(label);
1382 if (label == lastUsed) {
1383 this.identityList.selectedItem = opt;
1384 foundLastUsed = true;
1387 if (!foundLastUsed) {
1388 this.identityList.selectedIndex = -1;
1393 <method name="_onButtonCommand">
1394 <parameter name="aEvent"/>
1396 if (aEvent.target != aEvent.currentTarget)
1399 switch (this.step) {
1401 aEvent.stopPropagation();
1402 if (!this.emailField.validity.valid) {
1403 this.emailField.focus();
1406 chosenId = this.emailField.value;
1409 aEvent.stopPropagation();
1410 let selectedItem = this.identityList.selectedItem
1411 chosenId = selectedItem ? selectedItem.label : null;
1416 throw new Error("Unknown case");
1419 // Actually select the identity
1420 this.SignInToWebsiteUX.selectIdentity(this.identity.rpId, chosenId);
1421 this.identity.selected = chosenId;
1422 this.onIdentitySelected();
1429 <binding id="plugin-popupnotification-center-item">
1430 <content align="center">
1431 <xul:vbox pack="center" anonid="itemBox" class="itemBox">
1432 <xul:description anonid="center-item-label" class="center-item-label" />
1433 <xul:hbox flex="1" pack="start" align="center" anonid="center-item-warning">
1434 <xul:image anonid="center-item-warning-icon" class="center-item-warning-icon"/>
1435 <xul:label anonid="center-item-warning-label"/>
1436 <xul:label anonid="center-item-link" value="&checkForUpdates;" class="text-link"/>
1439 <xul:vbox pack="center">
1440 <xul:menulist class="center-item-menulist"
1441 anonid="center-item-menulist">
1443 <xul:menuitem anonid="allownow" value="allownow"
1444 label="&pluginActivateNow.label;" />
1445 <xul:menuitem anonid="allowalways" value="allowalways"
1446 label="&pluginActivateAlways.label;" />
1447 <xul:menuitem anonid="block" value="block"
1448 label="&pluginBlockNow.label;" />
1454 <stylesheet src="chrome://global/skin/notification.css"/>
1457 <constructor><![CDATA[
1458 document.getAnonymousElementByAttribute(this, "anonid", "center-item-label").value = this.action.pluginName;
1460 let curState = "block";
1461 if (this.action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
1462 if (this.action.pluginPermissionType == Ci.nsIPermissionManager.EXPIRE_SESSION) {
1463 curState = "allownow";
1466 curState = "allowalways";
1469 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").value = curState;
1471 let warningString = "";
1472 let linkString = "";
1474 let link = document.getAnonymousElementByAttribute(this, "anonid", "center-item-link");
1479 if (this.action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) {
1480 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true;
1481 warningString = gNavigatorBundle.getString("pluginActivateDisabled.label");
1482 linkString = gNavigatorBundle.getString("pluginActivateDisabled.manage");
1483 linkHandler = function(event) {
1484 event.preventDefault();
1485 gPluginHandler.managePlugins();
1487 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-icon").hidden = true;
1490 url = this.action.detailsLink;
1492 switch (this.action.blocklistState) {
1493 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
1494 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning").hidden = true;
1496 case Ci.nsIBlocklistService.STATE_BLOCKED:
1497 document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true;
1498 warningString = gNavigatorBundle.getString("pluginActivateBlocked.label");
1499 linkString = gNavigatorBundle.getString("pluginActivate.learnMore");
1501 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
1502 warningString = gNavigatorBundle.getString("pluginActivateOutdated.label");
1503 linkString = gNavigatorBundle.getString("pluginActivate.updateLabel");
1505 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
1506 warningString = gNavigatorBundle.getString("pluginActivateVulnerable.label");
1507 linkString = gNavigatorBundle.getString("pluginActivate.riskLabel");
1511 document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString;
1513 if (url || linkHandler) {
1514 link.value = linkString;
1519 link.addEventListener("click", linkHandler, false);
1526 <property name="value">
1528 return document.getAnonymousElementByAttribute(this, "anonid",
1529 "center-item-menulist").value;
1531 <setter><!-- This should be used only in automated tests -->
1532 document.getAnonymousElementByAttribute(this, "anonid",
1533 "center-item-menulist").value = val;
1539 <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
1540 <content align="start" style="width: &pluginNotification.width;;">
1541 <xul:vbox flex="1" align="stretch" class="popup-notification-main-box"
1542 xbl:inherits="popupid">
1543 <xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start">
1544 <xul:description class="click-to-play-plugins-outer-description" flex="1">
1545 <html:span anonid="click-to-play-plugins-notification-description" />
1546 <xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" />
1548 <xul:toolbarbutton anonid="closebutton"
1549 class="messageCloseButton popup-notification-closebutton tabbable close-icon"
1550 xbl:inherits="oncommand=closebuttoncommand"
1551 tooltiptext="&closeNotification.tooltip;"/>
1553 <xul:grid anonid="click-to-play-plugins-notification-center-box"
1554 class="click-to-play-plugins-notification-center-box">
1556 <xul:column flex="1"/>
1560 <children includes="row"/>
1561 <xul:hbox pack="start" anonid="plugin-notification-showbox">
1562 <xul:button label="&pluginNotification.showAll.label;"
1563 accesskey="&pluginNotification.showAll.accesskey;"
1564 class="plugin-notification-showbutton"
1565 oncommand="document.getBindingParent(this)._setState(2)"/>
1569 <xul:hbox anonid="button-container"
1570 class="click-to-play-plugins-notification-button-container"
1571 pack="center" align="center">
1572 <xul:button anonid="primarybutton"
1573 class="click-to-play-popup-button"
1574 oncommand="document.getBindingParent(this)._onButton(this)"
1576 <xul:button anonid="secondarybutton"
1577 class="click-to-play-popup-button"
1578 oncommand="document.getBindingParent(this)._onButton(this);"
1581 <xul:box hidden="true">
1587 <stylesheet src="chrome://global/skin/notification.css"/>
1590 <field name="_states">
1591 ({SINGLE: 0, MULTI_COLLAPSED: 1, MULTI_EXPANDED: 2})
1593 <field name="_primaryButton">
1594 document.getAnonymousElementByAttribute(this, "anonid", "primarybutton");
1596 <field name="_secondaryButton">
1597 document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton")
1599 <field name="_buttonContainer">
1600 document.getAnonymousElementByAttribute(this, "anonid", "button-container")
1602 <field name="_brandShortName">
1603 document.getElementById("bundle_brand").getString("brandShortName")
1605 <field name="_items">[]</field>
1606 <constructor><![CDATA[
1607 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
1608 let sortedActions = [];
1609 for (let action of this.notification.options.pluginData.values()) {
1610 sortedActions.push(action);
1612 sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName));
1614 for (let action of sortedActions) {
1615 let item = document.createElementNS(XUL_NS, "row");
1616 item.setAttribute("class", "plugin-popupnotification-centeritem");
1617 item.action = action;
1618 this.appendChild(item);
1619 this._items.push(item);
1621 switch (this._items.length) {
1623 PopupNotifications._dismiss();
1626 this._setState(this._states.SINGLE);
1629 if (this.notification.options.primaryPlugin) {
1630 this._setState(this._states.MULTI_COLLAPSED);
1632 this._setState(this._states.MULTI_EXPANDED);
1636 <method name="_setState">
1637 <parameter name="state" />
1639 var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box");
1641 if (this._states.SINGLE == state) {
1643 this._setupSingleState();
1647 let host = gPluginHandler._getHostFromPrincipal(this.notification.browser.contentWindow.document.nodePrincipal);
1648 this._setupDescription("pluginActivateMultiple.message", null, host);
1650 var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox");
1652 var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties");
1653 this._primaryButton.label = dialogStrings.GetStringFromName("button-accept");
1654 this._primaryButton.setAttribute("default", "true");
1656 this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel");
1657 this._primaryButton.setAttribute("action", "_multiAccept");
1658 this._secondaryButton.setAttribute("action", "_cancel");
1660 grid.hidden = false;
1662 if (this._states.MULTI_COLLAPSED == state) {
1663 for (let child of this.childNodes) {
1664 if (child.tagName != "row") {
1667 child.hidden = this.notification.options.primaryPlugin !=
1668 child.action.permissionString;
1670 showBox.hidden = false;
1673 for (let child of this.childNodes) {
1674 if (child.tagName != "row") {
1677 child.hidden = false;
1679 showBox.hidden = true;
1681 this._setupLink(null);
1684 <method name="_setupSingleState">
1686 var action = this._items[0].action;
1687 var host = action.pluginPermissionHost;
1689 let label, linkLabel, linkUrl, button1, button2;
1691 if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
1693 label: "pluginBlockNow.label",
1694 accesskey: "pluginBlockNow.accesskey",
1695 action: "_singleBlock"
1698 label: "pluginContinue.label",
1699 accesskey: "pluginContinue.accesskey",
1700 action: "_singleContinue",
1703 switch (action.blocklistState) {
1704 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
1705 label = "pluginEnabled.message";
1706 linkLabel = "pluginActivate.learnMore";
1709 case Ci.nsIBlocklistService.STATE_BLOCKED:
1710 Cu.reportError(Error("Cannot happen!"));
1713 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
1714 label = "pluginEnabledOutdated.message";
1715 linkLabel = "pluginActivate.updateLabel";
1718 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
1719 label = "pluginEnabledVulnerable.message";
1720 linkLabel = "pluginActivate.riskLabel"
1724 Cu.reportError(Error("Unexpected blocklist state"));
1727 else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) {
1729 document.getAnonymousElementByAttribute(
1730 this, "anonid", "click-to-play-plugins-notification-link");
1731 linkElement.textContent = gNavigatorBundle.getString("pluginActivateDisabled.manage");
1732 linkElement.setAttribute("onclick", "gPluginHandler.managePlugins()");
1734 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
1735 descElement.textContent = gNavigatorBundle.getFormattedString(
1736 "pluginActivateDisabled.message", [action.pluginName, this._brandShortName]) + " ";
1737 this._buttonContainer.hidden = true;
1740 else if (action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
1741 let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
1742 descElement.textContent = gNavigatorBundle.getFormattedString(
1743 "pluginActivateBlocked.message", [action.pluginName, this._brandShortName]) + " ";
1744 this._setupLink("pluginActivate.learnMore", action.detailsLink);
1745 this._buttonContainer.hidden = true;
1750 label: "pluginActivateNow.label",
1751 accesskey: "pluginActivateNow.accesskey",
1752 action: "_singleActivateNow"
1755 label: "pluginActivateAlways.label",
1756 accesskey: "pluginActivateAlways.accesskey",
1757 action: "_singleActivateAlways"
1759 switch (action.blocklistState) {
1760 case Ci.nsIBlocklistService.STATE_NOT_BLOCKED:
1761 label = "pluginActivateNew.message";
1762 linkLabel = "pluginActivate.learnMore";
1763 button2.default = true;
1766 case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
1767 label = "pluginActivateOutdated.message";
1768 linkLabel = "pluginActivate.updateLabel";
1769 button1.default = true;
1772 case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
1773 label = "pluginActivateVulnerable.message";
1774 linkLabel = "pluginActivate.riskLabel"
1775 button1.default = true;
1779 Cu.reportError(Error("Unexpected blocklist state"));
1782 this._setupDescription(label, action.pluginName, host);
1783 this._setupLink(linkLabel, action.detailsLink);
1785 this._primaryButton.label = gNavigatorBundle.getString(button1.label);
1786 this._primaryButton.accesskey = gNavigatorBundle.getString(button1.accesskey);
1787 this._primaryButton.setAttribute("action", button1.action);
1789 this._secondaryButton.label = gNavigatorBundle.getString(button2.label);
1790 this._secondaryButton.accesskey = gNavigatorBundle.getString(button2.accesskey);
1791 this._secondaryButton.setAttribute("action", button2.action);
1792 if (button1.default) {
1793 this._primaryButton.setAttribute("default", "true");
1795 else if (button2.default) {
1796 this._secondaryButton.setAttribute("default", "true");
1800 <method name="_setupDescription">
1801 <parameter name="baseString" />
1802 <parameter name="pluginName" /> <!-- null for the multiple-plugin case -->
1803 <parameter name="host" />
1805 var bsn = this._brandShortName;
1806 var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
1807 while (span.lastChild) {
1808 span.removeChild(span.lastChild);
1811 var args = ["__host__", this._brandShortName];
1813 args.unshift(pluginName);
1815 var bases = gNavigatorBundle.getFormattedString(baseString, args).
1816 split("__host__", 2);
1818 span.appendChild(document.createTextNode(bases[0]));
1819 var hostSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em");
1820 hostSpan.appendChild(document.createTextNode(host));
1821 span.appendChild(hostSpan);
1822 span.appendChild(document.createTextNode(bases[1] + " "));
1825 <method name="_setupLink">
1826 <parameter name="linkString"/>
1827 <parameter name="linkUrl" />
1829 var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link");
1830 if (!linkString || !linkUrl) {
1835 link.hidden = false;
1836 link.textContent = gNavigatorBundle.getString(linkString);
1837 link.href = linkUrl;
1840 <method name="_onButton">
1841 <parameter name="aButton" />
1843 let methodName = aButton.getAttribute("action");
1847 <method name="_singleActivateNow">
1849 gPluginHandler._updatePluginPermission(this.notification,
1850 this._items[0].action,
1855 <method name="_singleBlock">
1857 gPluginHandler._updatePluginPermission(this.notification,
1858 this._items[0].action,
1863 <method name="_singleActivateAlways">
1865 gPluginHandler._updatePluginPermission(this.notification,
1866 this._items[0].action,
1871 <method name="_singleContinue">
1873 gPluginHandler._updatePluginPermission(this.notification,
1874 this._items[0].action,
1879 <method name="_multiAccept">
1881 for (let item of this._items) {
1882 let action = item.action;
1883 if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED ||
1884 action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
1887 gPluginHandler._updatePluginPermission(this.notification,
1888 item.action, item.value);
1893 <method name="_cancel">
1895 PopupNotifications._dismiss();
1898 <method name="_accept">
1899 <parameter name="aEvent" />
1901 if (aEvent.defaultPrevented)
1903 aEvent.preventDefault();
1904 if (this._primaryButton.getAttribute("default") == "true") {
1905 this._primaryButton.click();
1907 else if (this._secondaryButton.getAttribute("default") == "true") {
1908 this._secondaryButton.click();
1914 <!-- The _accept method checks for .defaultPrevented so that if focus is in a button,
1915 enter activates the button and not this default action -->
1916 <handler event="keypress" keycode="VK_RETURN" group="system" action="this._accept(event);"/>
1920 <binding id="splitmenu">
1922 <xul:hbox anonid="menuitem" flex="1"
1923 class="splitmenu-menuitem"
1924 xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/>
1925 <xul:menu anonid="menu" class="splitmenu-menu"
1926 xbl:inherits="disabled,_moz-menuactive=active"
1927 oncommand="event.stopPropagation();">
1928 <children includes="menupopup"/>
1932 <implementation implements="nsIDOMEventListener">
1933 <constructor><![CDATA[
1934 this._parentMenupopup.addEventListener("DOMMenuItemActive", this, false);
1935 this._parentMenupopup.addEventListener("popuphidden", this, false);
1938 <destructor><![CDATA[
1939 this._parentMenupopup.removeEventListener("DOMMenuItemActive", this, false);
1940 this._parentMenupopup.removeEventListener("popuphidden", this, false);
1943 <field name="menuitem" readonly="true">
1944 document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
1946 <field name="menu" readonly="true">
1947 document.getAnonymousElementByAttribute(this, "anonid", "menu");
1950 <field name="_menuDelay">600</field>
1952 <field name="_parentMenupopup"><![CDATA[
1953 this._getParentMenupopup(this);
1956 <method name="_getParentMenupopup">
1957 <parameter name="aNode"/>
1959 let node = aNode.parentNode;
1961 if (node.localName == "menupopup")
1963 node = node.parentNode;
1969 <method name="handleEvent">
1970 <parameter name="event"/>
1972 switch (event.type) {
1973 case "DOMMenuItemActive":
1974 if (this.getAttribute("active") == "true" &&
1975 event.target != this &&
1976 this._getParentMenupopup(event.target) == this._parentMenupopup)
1977 this.removeAttribute("active");
1980 if (event.target == this._parentMenupopup)
1981 this.removeAttribute("active");
1989 <handler event="mouseover"><![CDATA[
1990 if (this.getAttribute("active") != "true") {
1991 this.setAttribute("active", "true");
1993 let event = document.createEvent("Events");
1994 event.initEvent("DOMMenuItemActive", true, false);
1995 this.dispatchEvent(event);
1997 if (this.getAttribute("disabled") != "true") {
1999 setTimeout(function () {
2000 if (self.getAttribute("active") == "true")
2001 self.menu.open = true;
2002 }, this._menuDelay);
2007 <handler event="popupshowing"><![CDATA[
2008 if (event.target == this.firstChild &&
2009 this._parentMenupopup._currentPopup)
2010 this._parentMenupopup._currentPopup.hidePopup();
2013 <handler event="click" phase="capturing"><![CDATA[
2014 if (this.getAttribute("disabled") == "true") {
2015 // Prevent the command from being carried out
2016 event.stopPropagation();
2020 let node = event.originalTarget;
2022 if (node == this.menuitem)
2026 node = node.parentNode;
2029 this._parentMenupopup.hidePopup();
2034 <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
2036 <constructor><![CDATA[
2037 this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
2038 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
2040 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
2045 <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic">
2047 <constructor><![CDATA[
2048 this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
2049 // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
2051 document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
2056 <binding id="promobox">
2058 <xul:hbox class="panel-promo-box" align="start" flex="1">
2059 <xul:hbox align="center" flex="1">
2060 <xul:image class="panel-promo-icon"/>
2061 <xul:description anonid="promo-message" class="panel-promo-message" flex="1">
2062 <xul:description anonid="promo-link"
2063 class="plain text-link inline-link"
2064 onclick="document.getBindingParent(this).onLinkClick();"/>
2067 <xul:toolbarbutton class="panel-promo-closebutton close-icon"
2068 oncommand="document.getBindingParent(this).onCloseButtonCommand();"
2069 tooltiptext="&closeNotification.tooltip;"/>
2073 <implementation implements="nsIDOMEventListener">
2074 <constructor><![CDATA[
2075 this._panel.addEventListener("popupshowing", this, false);
2078 <destructor><![CDATA[
2079 this._panel.removeEventListener("popupshowing", this, false);
2082 <field name="_panel" readonly="true"><![CDATA[
2083 let node = this.parentNode;
2084 while(node && node.localName != "panel") {
2085 node = node.parentNode;
2089 <field name="_promomessage" readonly="true">
2090 document.getAnonymousElementByAttribute(this, "anonid", "promo-message");
2092 <field name="_promolink" readonly="true">
2093 document.getAnonymousElementByAttribute(this, "anonid", "promo-link");
2095 <field name="_brandBundle" readonly="true">
2096 Services.strings.createBundle("chrome://branding/locale/brand.properties");
2098 <property name="_viewsLeftMap">
2101 return JSON.parse(Services.prefs.getCharPref("browser.syncPromoViewsLeftMap"));
2106 <property name="_viewsLeft">
2109 let map = this._viewsLeftMap;
2110 if (this._notificationType in map) {
2111 views = map[this._notificationType];
2116 let map = this._viewsLeftMap;
2117 map[this._notificationType] = val;
2118 Services.prefs.setCharPref("browser.syncPromoViewsLeftMap",
2119 JSON.stringify(map));
2123 <property name="_notificationType">
2125 // Use the popupid attribute to identify the notification type,
2126 // otherwise just rely on the panel id for common arrowpanels.
2127 let type = this._panel.firstChild.getAttribute("popupid") ||
2129 if (type.startsWith("password-"))
2131 if (type == "editBookmarkPanel")
2133 if (type == "addon-install-complete") {
2134 if (!Services.prefs.prefHasUserValue("services.sync.username"))
2136 if (!Services.prefs.getBoolPref("services.sync.engine.addons"))
2137 return "addons-sync-disabled";
2142 <property name="_notificationMessage">
2144 return gNavigatorBundle.getFormattedString(
2145 "syncPromoNotification." + this._notificationType + ".description",
2146 [this._brandBundle.GetStringFromName("syncBrandShortName")]
2150 <property name="_notificationLink">
2152 if (this._notificationType == "addons-sync-disabled") {
2153 return "https://support.mozilla.org/kb/how-do-i-enable-add-sync";
2155 return "https://services.mozilla.com/sync/";
2158 <method name="onCloseButtonCommand">
2160 this._viewsLeft = 0;
2164 <method name="onLinkClick">
2166 // Open a new selected tab and close the current panel.
2167 openUILinkIn(this._promolink.getAttribute("href"), "tab");
2168 this._panel.hidePopup();
2171 <method name="handleEvent">
2172 <parameter name="event"/>
2174 if (event.type != "popupshowing" || event.target != this._panel)
2177 // A previous notification may have unhidden this.
2180 // Only handle supported notification panels.
2181 if (!this._notificationType) {
2185 let viewsLeft = this._viewsLeft;
2187 if (Services.prefs.prefHasUserValue("services.sync.username") &&
2188 this._notificationType != "addons-sync-disabled") {
2189 // If the user has already setup Sync, don't show the notification.
2190 this._viewsLeft = 0;
2191 // Be sure to hide the panel, in case it was visible and the user
2192 // decided to setup Sync after noticing it.
2194 // The panel is still hidden, just bail out.
2198 this._viewsLeft = viewsLeft - 1;
2201 this._promolink.setAttribute("href", this._notificationLink);
2202 this._promolink.value = gNavigatorBundle.getString("syncPromoNotification.learnMoreLinkText");
2204 this.hidden = false;
2206 // HACK: The description element doesn't wrap correctly in panels,
2207 // thus set a width on it, based on the available space, before
2208 // setting its textContent. Then set its height as well, to
2209 // fix wrong height calculation on Linux (bug 659578).
2210 this._panel.addEventListener("popupshown", function panelShown() {
2211 this._panel.removeEventListener("popupshown", panelShown, true);
2212 // Previous popupShown events may close the panel or change
2213 // its contents, so ensure this is still valid.
2214 if (this._panel.state != "open" || !this._notificationType)
2216 this._promomessage.width = this._promomessage.getBoundingClientRect().width;
2217 this._promomessage.firstChild.textContent = this._notificationMessage;
2218 this._promomessage.height = this._promomessage.getBoundingClientRect().height;
2219 }.bind(this), true);
2226 <binding id="toolbarbutton-badged" display="xul:button"
2227 extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
2229 <children includes="observes|template|menupopup|panel|tooltip"/>
2230 <xul:hbox class="toolbarbutton-badge-container" align="start" pack="end">
2231 <xul:hbox class="toolbarbutton-badge" xbl:inherits="badge"/>
2232 <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
2234 <xul:label class="toolbarbutton-text" crop="right" flex="1"
2235 xbl:inherits="value=label,accesskey,crop,wrap"/>
2236 <xul:label class="toolbarbutton-multiline-text" flex="1"
2237 xbl:inherits="xbl:text=label,accesskey,wrap"/>