diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 15:50:03 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 15:50:03 +0200 |
commit | e3b7744bee37c3d4a026d2193bed5e9439c40ff3 (patch) | |
tree | f3f7b07ca9bd78bf7ac2d76dd55b61b2a8bb549e /browser/base/content/urlbarBindings.xml | |
parent | cbce4f0b6a337f8250b62cae028f1c6d4cce51df (diff) | |
parent | 031afcafe288bf0f46c0c5caae20dd3db8bd0297 (diff) | |
download | UXP-e3b7744bee37c3d4a026d2193bed5e9439c40ff3.tar UXP-e3b7744bee37c3d4a026d2193bed5e9439c40ff3.tar.gz UXP-e3b7744bee37c3d4a026d2193bed5e9439c40ff3.tar.lz UXP-e3b7744bee37c3d4a026d2193bed5e9439c40ff3.tar.xz UXP-e3b7744bee37c3d4a026d2193bed5e9439c40ff3.zip |
Merge branch 'move-basilisk'
Diffstat (limited to 'browser/base/content/urlbarBindings.xml')
-rw-r--r-- | browser/base/content/urlbarBindings.xml | 2758 |
1 files changed, 0 insertions, 2758 deletions
diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml deleted file mode 100644 index eb3150581..000000000 --- a/browser/base/content/urlbarBindings.xml +++ /dev/null @@ -1,2758 +0,0 @@ -<?xml version="1.0"?> - -<!-- --*- Mode: HTML -*- -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at http://mozilla.org/MPL/2.0/. ---> - -<!DOCTYPE bindings [ -<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> -%notificationDTD; -<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> -%browserDTD; -<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> -%brandDTD; -]> - -<bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> - - <content sizetopopup="pref"> - <xul:hbox anonid="textbox-container" - class="autocomplete-textbox-container urlbar-textbox-container" - flex="1" xbl:inherits="focused"> - <children includes="image|deck|stack|box"> - <xul:image class="autocomplete-icon" allowevents="true"/> - </children> - <xul:hbox anonid="textbox-input-box" - class="textbox-input-box urlbar-input-box" - flex="1" xbl:inherits="tooltiptext=inputtooltiptext"> - <children/> - <html:input anonid="input" - class="autocomplete-textbox urlbar-input textbox-input uri-element-right-align" - allowevents="true" - inputmode="url" - xbl:inherits="tooltiptext=inputtooltiptext,value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/> - </xul:hbox> - <xul:dropmarker anonid="historydropmarker" - class="autocomplete-history-dropmarker urlbar-history-dropmarker" - tooltiptext="&urlbar.openHistoryPopup.tooltip;" - allowevents="true" - xbl:inherits="open,enablehistory,parentfocused=focused"/> - <children includes="hbox"/> - </xul:hbox> - <xul:popupset anonid="popupset" - class="autocomplete-result-popupset"/> - <children includes="toolbarbutton"/> - </content> - - <implementation implements="nsIObserver, nsIDOMEventListener"> - <field name="AppConstants" readonly="true"> - (Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants; - </field> -#ifdef MOZ_WEBEXTENSIONS - <field name="ExtensionSearchHandler" readonly="true"> - (Components.utils.import("resource://gre/modules/ExtensionSearchHandler.jsm", {})).ExtensionSearchHandler; - </field> -#endif - - <constructor><![CDATA[ - this._prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService) - .getBranch("browser.urlbar."); - - this._prefs.addObserver("", this, false); - this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll"); - this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll"); - this.completeDefaultIndex = this._prefs.getBoolPref("autoFill"); - this.timeout = this._prefs.getIntPref("delay"); - this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled"); - this._mayTrimURLs = this._prefs.getBoolPref("trimURLs"); - this._cacheUserMadeSearchSuggestionsChoice(); - this.inputField.controllers.insertControllerAt(0, this._copyCutController); - this.inputField.addEventListener("paste", this, false); - this.inputField.addEventListener("mousedown", this, false); - this.inputField.addEventListener("mousemove", this, false); - this.inputField.addEventListener("mouseout", this, false); - this.inputField.addEventListener("overflow", this, false); - this.inputField.addEventListener("underflow", this, false); - - var textBox = document.getAnonymousElementByAttribute(this, - "anonid", "textbox-input-box"); - var cxmenu = document.getAnonymousElementByAttribute(textBox, - "anonid", "input-box-contextmenu"); - var pasteAndGo; - cxmenu.addEventListener("popupshowing", function() { - if (!pasteAndGo) - return; - var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); - var enabled = controller.isCommandEnabled("cmd_paste"); - if (enabled) - pasteAndGo.removeAttribute("disabled"); - else - pasteAndGo.setAttribute("disabled", "true"); - }, false); - - var insertLocation = cxmenu.firstChild; - while (insertLocation.nextSibling && - insertLocation.getAttribute("cmd") != "cmd_paste") - insertLocation = insertLocation.nextSibling; - if (insertLocation) { - pasteAndGo = document.createElement("menuitem"); - let label = Services.strings.createBundle("chrome://browser/locale/browser.properties"). - GetStringFromName("pasteAndGo.label"); - pasteAndGo.setAttribute("label", label); - pasteAndGo.setAttribute("anonid", "paste-and-go"); - pasteAndGo.setAttribute("oncommand", - "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();"); - cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling); - } - - this._enableOrDisableOneOffSearches(); - ]]></constructor> - - <destructor><![CDATA[ - this._prefs.removeObserver("", this); - this._prefs = null; - this.inputField.controllers.removeController(this._copyCutController); - this.inputField.removeEventListener("paste", this, false); - this.inputField.removeEventListener("mousedown", this, false); - this.inputField.removeEventListener("mousemove", this, false); - this.inputField.removeEventListener("mouseout", this, false); - this.inputField.removeEventListener("overflow", this, false); - this.inputField.removeEventListener("underflow", this, false); - ]]></destructor> - - <field name="_value">""</field> - <field name="gotResultForCurrentQuery">false</field> - - <!-- - This is set around HandleHenter so it can be used in handleCommand. - It is also used to track whether we must handle a delayed handleEnter, - by checking if it has been cleared. - --> - <field name="handleEnterInstance">null</field> - - <!-- - For performance reasons we want to limit the size of the text runs we - build and show to the user. - --> - <field name="textRunsMaxLen">255</field> - - <!-- - onBeforeValueGet is called by the base-binding's .value getter. - It can return an object with a "value" property, to override the - return value of the getter. - --> - <method name="onBeforeValueGet"> - <body><![CDATA[ - return { value: this._value }; - ]]></body> - </method> - - <!-- - onBeforeValueSet is called by the base-binding's .value setter. - It should return the value that the setter should use. - --> - <method name="onBeforeValueSet"> - <parameter name="aValue"/> - <body><![CDATA[ - this._value = aValue; - var returnValue = aValue; - var action = this._parseActionUrl(aValue); - - if (action) { - switch (action.type) { - case "switchtab": // Fall through. - case "remotetab": // Fall through. - case "visiturl": { - returnValue = action.params.displayUrl; - break; - } - case "keyword": // Fall through. - case "searchengine": { - returnValue = action.params.input; - break; - } - case "extension": { - returnValue = action.params.content; - break; - } - } - } else { - let originalUrl = ReaderMode.getOriginalUrl(aValue); - if (originalUrl) { - returnValue = originalUrl; - } - } - - // Set the actiontype only if the user is not overriding actions. - if (action && this._pressedNoActionKeys.size == 0) { - this.setAttribute("actiontype", action.type); - } else { - this.removeAttribute("actiontype"); - } - return returnValue; - ]]></body> - </method> - - <method name="onKeyPress"> - <parameter name="aEvent"/> - <body><![CDATA[ - switch (aEvent.keyCode) { - case KeyEvent.DOM_VK_LEFT: - case KeyEvent.DOM_VK_RIGHT: - case KeyEvent.DOM_VK_HOME: - // Reset the selected index so that nsAutoCompleteController - // simply closes the popup without trying to fill anything. - this.popup.selectedIndex = -1; - break; - } - if (this.popup.popupOpen && - !this.popup.disableKeyNavigation && - this.popup.handleKeyPress(aEvent)) { - return true; - } - return this.handleKeyPress(aEvent); - ]]></body> - </method> - - <field name="_mayTrimURLs">true</field> - <method name="trimValue"> - <parameter name="aURL"/> - <body><![CDATA[ - // This method must not modify the given URL such that calling - // nsIURIFixup::createFixupURI with the result will produce a different URI. - return this._mayTrimURLs ? trimURL(aURL) : aURL; - ]]></body> - </method> - - <field name="_formattingEnabled">true</field> - <method name="formatValue"> - <body><![CDATA[ - if (!this._formattingEnabled || !this.editor) - return; - - let controller = this.editor.selectionController; - let strikeOut = controller.getSelection(controller.SELECTION_URLSTRIKEOUT); - strikeOut.removeAllRanges(); - - let selection = controller.getSelection(controller.SELECTION_URLSECONDARY); - selection.removeAllRanges(); - - if (this.focused) - return; - - let textNode = this.editor.rootElement.firstChild; - let value = textNode.textContent; - if (!value) - return; - - // Get the URL from the fixup service: - let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | - Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; - let uriInfo; - try { - uriInfo = Services.uriFixup.getFixupURIInfo(value, flags); - } catch (ex) {} - // Ignore if we couldn't make a URI out of this, the URI resulted in a search, - // or the URI has a non-http(s)/ftp protocol. - if (!uriInfo || - !uriInfo.fixedURI || - uriInfo.keywordProviderName || - ["http", "https", "ftp"].indexOf(uriInfo.fixedURI.scheme) == -1) { - return; - } - - // If we trimmed off the http scheme, ensure we stick it back on before - // trying to figure out what domain we're accessing, so we don't get - // confused by user:pass@host http URLs. We later use - // trimmedLength to ensure we don't count the length of a trimmed protocol - // when determining which parts of the URL to highlight as "preDomain". - let trimmedLength = 0; - if (uriInfo.fixedURI.scheme == "http" && !value.startsWith("http://")) { - value = "http://" + value; - trimmedLength = "http://".length; - } - - let matchedURL = value.match(/^((?:[a-z]+:\/\/)(?:[^\/#?]+@)?)(\S+?)(?::\d+)?\s*(?:[\/#?]|$)/); - if (!matchedURL) - return; - - // Strike out the "https" part if mixed active content is loaded. - if (this.getAttribute("pageproxystate") == "valid" && - value.startsWith("https:") && - gBrowser.securityUI.state & - Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) { - let range = document.createRange(); - range.setStart(textNode, 0); - range.setEnd(textNode, 5); - strikeOut.addRange(range); - } - - let [, preDomain, domain] = matchedURL; - let baseDomain = domain; - let subDomain = ""; - try { - baseDomain = Services.eTLD.getBaseDomainFromHost(uriInfo.fixedURI.host); - if (!domain.endsWith(baseDomain)) { - // getBaseDomainFromHost converts its resultant to ACE. - let IDNService = Cc["@mozilla.org/network/idn-service;1"] - .getService(Ci.nsIIDNService); - baseDomain = IDNService.convertACEtoUTF8(baseDomain); - } - } catch (e) {} - if (baseDomain != domain) { - subDomain = domain.slice(0, -baseDomain.length); - } - - let rangeLength = preDomain.length + subDomain.length - trimmedLength; - if (rangeLength) { - let range = document.createRange(); - range.setStart(textNode, 0); - range.setEnd(textNode, rangeLength); - selection.addRange(range); - } - - let startRest = preDomain.length + domain.length - trimmedLength; - if (startRest < value.length - trimmedLength) { - let range = document.createRange(); - range.setStart(textNode, startRest); - range.setEnd(textNode, value.length - trimmedLength); - selection.addRange(range); - } - ]]></body> - </method> - - <method name="handleRevert"> - <body><![CDATA[ - var isScrolling = this.popupOpen; - - gBrowser.userTypedValue = null; - - // don't revert to last valid url unless page is NOT loading - // and user is NOT key-scrolling through autocomplete list - if (!XULBrowserWindow.isBusy && !isScrolling) { - URLBarSetURI(); - - // If the value isn't empty and the urlbar has focus, select the value. - if (this.value && this.hasAttribute("focused")) - this.select(); - } - - // tell widget to revert to last typed text only if the user - // was scrolling when they hit escape - return !isScrolling; - ]]></body> - </method> - - <!-- - This is ultimately called by the autocomplete controller as the result - of handleEnter when the Return key is pressed in the textbox. Since - onPopupClick also calls handleEnter, this is also called as a result in - that case. - - @param event - The event that triggered the command. - @param openUILinkWhere - Optional. The "where" to pass to openUILinkIn. This method - computes the appropriate "where" given the event, but you can - use this to override it. - @param openUILinkParams - Optional. The parameters to pass to openUILinkIn. As with - "where", this method computes the appropriate parameters, but - any parameters you supply here will override those. - --> - <method name="handleCommand"> - <parameter name="event"/> - <parameter name="openUILinkWhere"/> - <parameter name="openUILinkParams"/> - <body><![CDATA[ - let isMouseEvent = event instanceof MouseEvent; - if (isMouseEvent && event.button == 2) { - // Do nothing for right clicks. - return; - } - - // Determine whether to use the selected one-off search button. In - // one-off search buttons parlance, "selected" means that the button - // has been navigated to via the keyboard. So we want to use it if - // the triggering event is not a mouse click -- i.e., it's a Return - // key -- or if the one-off was mouse-clicked. - let selectedOneOff = this.popup.oneOffSearchButtons.selectedButton; - if (selectedOneOff && - isMouseEvent && - event.originalTarget != selectedOneOff) { - selectedOneOff = null; - } - - // Do the command of the selected one-off if it's not an engine. - if (selectedOneOff && !selectedOneOff.engine) { - selectedOneOff.doCommand(); - return; - } - - let where = openUILinkWhere; - if (!where) { - if (isMouseEvent) { - where = whereToOpenLink(event, false, false); - } else { - // If the current tab is empty, ignore Alt+Enter (reuse this tab) - let altEnter = !isMouseEvent && - event && - event.altKey && - !isTabEmpty(gBrowser.selectedTab); - where = altEnter ? "tab" : "current"; - } - } - - let url = this.value; - if (!url) { - return; - } - - let mayInheritPrincipal = false; - let postData = null; - let browser = gBrowser.selectedBrowser; - let action = this._parseActionUrl(url); - - if (selectedOneOff && selectedOneOff.engine) { - // If there's a selected one-off button then load a search using - // the one-off's engine. - [url, postData] = - this._parseAndRecordSearchEngineLoad(selectedOneOff.engine, - this.oneOffSearchQuery, - event, where, - openUILinkParams); - } else if (action) { - switch (action.type) { - case "visiturl": - // Unifiedcomplete uses fixupURI to tell if something is a visit - // or a search, and passes out the fixedURI as the url param. - // By using that uri we would end up passing a different string - // to the docshell that may run a different not-found heuristic. - // For example, "mozilla/run" would be fixed by unifiedcomplete - // to "http://mozilla/run". The docshell, once it can't resolve - // mozilla, would note the string has a scheme, and try to load - // http://mozilla.com/run instead of searching "mozilla/run". - // So, if we have the original input at hand, we pass it through - // and let the docshell handle it. - if (action.params.input) { - url = action.params.input; - break; - } - url = action.params.url; - break; - case "remotetab": - url = action.params.url; - break; - case "keyword": - if (action.params.postData) { - postData = getPostDataStream(action.params.postData); - } - mayInheritPrincipal = true; - url = action.params.url; - break; - case "switchtab": - url = action.params.url; - if (this.hasAttribute("actiontype")) { - this.handleRevert(); - let prevTab = gBrowser.selectedTab; - if (switchToTabHavingURI(url) && isTabEmpty(prevTab)) { - gBrowser.removeTab(prevTab); - } - return; - } - break; - case "searchengine": - if (selectedOneOff && selectedOneOff.engine) { - // Replace the engine with the selected one-off engine. - action.params.engineName = selectedOneOff.engine.name; - } - const actionDetails = { - isSuggestion: !!action.params.searchSuggestion, - isAlias: !!action.params.alias - }; - [url, postData] = this._parseAndRecordSearchEngineLoad( - action.params.engineName, - action.params.searchSuggestion || action.params.searchQuery, - event, - where, - openUILinkParams, - actionDetails - ); - break; -#ifdef MOZ_WEBEXTENSIONS - case "extension": - this.handleRevert(); - // Give the extension control of handling the command. - let searchString = action.params.content; - let keyword = action.params.keyword; - this.ExtensionSearchHandler.handleInputEntered(keyword, searchString, where); - return; -#endif - } - } else { - // This is a fallback for add-ons and old testing code that directly - // set value and try to confirm it. UnifiedComplete should always - // resolve to a valid url. - try { - new URL(url); - } catch (ex) { - let lastLocationChange = browser.lastLocationChange; - getShortcutOrURIAndPostData(url).then(data => { - if (where != "current" || - browser.lastLocationChange == lastLocationChange) { - this._loadURL(data.url, browser, data.postData, where, - openUILinkParams, data.mayInheritPrincipal); - } - }); - return; - } - } - - this._loadURL(url, browser, postData, where, openUILinkParams, - mayInheritPrincipal); - ]]></body> - </method> - - <property name="oneOffSearchQuery"> - <getter><![CDATA[ - // this.textValue may be an autofilled string. Search only with the - // portion that the user typed, if any, by preferring the autocomplete - // controller's searchString (including handleEnterInstance.searchString). - return (this.handleEnterInstance && this.handleEnterInstance.searchString) || - this.mController.searchString || - this.textValue; - ]]></getter> - </property> - - <method name="_loadURL"> - <parameter name="url"/> - <parameter name="browser"/> - <parameter name="postData"/> - <parameter name="openUILinkWhere"/> - <parameter name="openUILinkParams"/> - <parameter name="mayInheritPrincipal"/> - <body><![CDATA[ - this.value = url; - browser.userTypedValue = url; - if (gInitialPages.includes(url)) { - browser.initialPageLoadedFromURLBar = url; - } - try { - addToUrlbarHistory(url); - } catch (ex) { - // Things may go wrong when adding url to session history, - // but don't let that interfere with the loading of the url. - Cu.reportError(ex); - } - - let params = { - postData, - allowThirdPartyFixup: true, - currentBrowser: browser, - }; - if (openUILinkWhere == "current") { - params.indicateErrorPageLoad = true; - params.allowPinnedTabHostChange = true; - params.disallowInheritPrincipal = !mayInheritPrincipal; - params.allowPopups = url.startsWith("javascript:"); - } else { - params.initiatingDoc = document; - } - - if (openUILinkParams) { - for (let key in openUILinkParams) { - params[key] = openUILinkParams[key]; - } - } - - // Focus the content area before triggering loads, since if the load - // occurs in a new tab, we want focus to be restored to the content - // area when the current tab is re-selected. - browser.focus(); - - if (openUILinkWhere != "current") { - this.handleRevert(); - } - - try { - openUILinkIn(url, openUILinkWhere, params); - } catch (ex) { - // This load can throw an exception in certain cases, which means - // we'll want to replace the URL with the loaded URL: - if (ex.result != Cr.NS_ERROR_LOAD_SHOWED_ERRORPAGE) { - this.handleRevert(); - } - } - - if (openUILinkWhere == "current") { - // Ensure the start of the URL is visible for usability reasons. - this.selectionStart = this.selectionEnd = 0; - } - ]]></body> - </method> - - <method name="_parseAndRecordSearchEngineLoad"> - <parameter name="engineOrEngineName"/> - <parameter name="query"/> - <parameter name="event"/> - <parameter name="openUILinkWhere"/> - <parameter name="openUILinkParams"/> - <parameter name="searchActionDetails"/> - <body><![CDATA[ - let engine = - typeof(engineOrEngineName) == "string" ? - Services.search.getEngineByName(engineOrEngineName) : - engineOrEngineName; - let isOneOff = this.popup.oneOffSearchButtons - .maybeRecordTelemetry(event, openUILinkWhere, openUILinkParams); - // Infer the type of the event which triggered the search. - let eventType = "unknown"; - if (event instanceof KeyboardEvent) { - eventType = "key"; - } else if (event instanceof MouseEvent) { - eventType = "mouse"; - } - // Augment the search action details object. - let details = searchActionDetails || {}; - details.isOneOff = isOneOff; - details.type = eventType; - - BrowserSearch.recordSearchInTelemetry(engine, "urlbar", details); - let submission = engine.getSubmission(query, null, "keyword"); - return [submission.uri.spec, submission.postData]; - ]]></body> - </method> - - <method name="maybeCanonizeURL"> - <parameter name="aTriggeringEvent"/> - <parameter name="aUrl"/> - <body><![CDATA[ - // Only add the suffix when the URL bar value isn't already "URL-like", - // and only if we get a keyboard event, to match user expectations. - if (!/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl) || - !(aTriggeringEvent instanceof KeyEvent)) { - return; - } - - let url = aUrl; - let accel = this.AppConstants.platform == "macosx" ? - aTriggeringEvent.metaKey : - aTriggeringEvent.ctrlKey; - let shift = aTriggeringEvent.shiftKey; - let suffix = ""; - - switch (true) { - case (accel && shift): - suffix = ".org/"; - break; - case (shift): - suffix = ".net/"; - break; - case (accel): - try { - suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix"); - if (suffix.charAt(suffix.length - 1) != "/") - suffix += "/"; - } catch (e) { - suffix = ".com/"; - } - break; - } - - if (!suffix) - return; - - // trim leading/trailing spaces (bug 233205) - url = url.trim(); - - // Tack www. and suffix on. If user has appended directories, insert - // suffix before them (bug 279035). Be careful not to get two slashes. - let firstSlash = url.indexOf("/"); - if (firstSlash >= 0) { - url = url.substring(0, firstSlash) + suffix + - url.substring(firstSlash + 1); - } else { - url = url + suffix; - } - - this.popup.overrideValue = "http://www." + url; - ]]></body> - </method> - - <field name="_contentIsCropped">false</field> - - <method name="_initURLTooltip"> - <body><![CDATA[ - if (this.focused || !this._contentIsCropped) - return; - this.inputField.setAttribute("tooltiptext", this.value); - ]]></body> - </method> - - <method name="_hideURLTooltip"> - <body><![CDATA[ - this.inputField.removeAttribute("tooltiptext"); - ]]></body> - </method> - - <method name="_getDroppableLink"> - <parameter name="aEvent"/> - <body><![CDATA[ - let links = browserDragAndDrop.dropLinks(aEvent); - // The URL bar automatically handles inputs with newline characters, - // so we can get away with treating text/x-moz-url flavours as text/plain. - if (links.length > 0 && links[0].url) { - aEvent.preventDefault(); - let url = links[0].url; - let strippedURL = stripUnsafeProtocolOnPaste(url); - if (strippedURL != url) { - aEvent.stopImmediatePropagation(); - return null; - } - try { - urlSecurityCheck(url, - gBrowser.contentPrincipal, - Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); - } catch (ex) { - return null; - } - return url; - } - return null; - ]]></body> - </method> - - <method name="onDragOver"> - <parameter name="aEvent"/> - <body><![CDATA[ - // We don't need the link here, so we ignore the return value. - if (!this._getDroppableLink(aEvent)) { - aEvent.dataTransfer.dropEffect = "none"; - } - ]]></body> - </method> - - <method name="onDrop"> - <parameter name="aEvent"/> - <body><![CDATA[ - let url = this._getDroppableLink(aEvent); - if (url) { - this.value = url; - SetPageProxyState("invalid"); - this.focus(); - this.handleCommand(); - // Force not showing the dropped URI immediately. - gBrowser.userTypedValue = null; - URLBarSetURI(); - } - ]]></body> - </method> - - <method name="_getSelectedValueForClipboard"> - <body><![CDATA[ - // Grab the actual input field's value, not our value, which could include moz-action: - var inputVal = this.inputField.value; - var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd); - - // If the selection doesn't start at the beginning or doesn't span the full domain or - // the URL bar is modified or there is no text at all, nothing else to do here. - if (this.selectionStart > 0 || this.valueIsTyped || selectedVal == "") - return selectedVal; - // The selection doesn't span the full domain if it doesn't contain a slash and is - // followed by some character other than a slash. - if (!selectedVal.includes("/")) { - let remainder = inputVal.replace(selectedVal, ""); - if (remainder != "" && remainder[0] != "/") - return selectedVal; - } - - let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup); - - let uri; - if (this.getAttribute("pageproxystate") == "valid") { - uri = gBrowser.currentURI; - } else { - // We're dealing with an autocompleted value, create a new URI from that. - try { - uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_NONE); - } catch (e) {} - if (!uri) - return selectedVal; - } - - // Avoid copying 'about:reader?url=', and always provide the original URI: - let readerOriginalURL = ReaderMode.getOriginalUrl(uri.spec); - if (readerOriginalURL) { - uri = uriFixup.createFixupURI(readerOriginalURL, Ci.nsIURIFixup.FIXUP_FLAG_NONE); - } - - // Only copy exposable URIs - try { - uri = uriFixup.createExposableURI(uri); - } catch (ex) {} - - // If the entire URL is selected, just use the actual loaded URI. - if (inputVal == selectedVal) { - // ... but only if isn't a javascript: or data: URI, since those - // are hard to read when encoded - if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) { - selectedVal = uri.spec; - } - - return selectedVal; - } - - // Just the beginning of the URL is selected, check for a trimmed - // value - let spec = uri.spec; - let trimmedSpec = this.trimValue(spec); - if (spec != trimmedSpec) { - // Prepend the portion that trimValue removed from the beginning. - // This assumes trimValue will only truncate the URL at - // the beginning or end (or both). - let trimmedSegments = spec.split(trimmedSpec); - selectedVal = trimmedSegments[0] + selectedVal; - } - - return selectedVal; - ]]></body> - </method> - - <field name="_copyCutController"><![CDATA[ - ({ - urlbar: this, - doCommand: function(aCommand) { - var urlbar = this.urlbar; - var val = urlbar._getSelectedValueForClipboard(); - if (!val) - return; - - if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) { - let start = urlbar.selectionStart; - let end = urlbar.selectionEnd; - urlbar.inputField.value = urlbar.inputField.value.substring(0, start) + - urlbar.inputField.value.substring(end); - urlbar.selectionStart = urlbar.selectionEnd = start; - - let event = document.createEvent("UIEvents"); - event.initUIEvent("input", true, false, window, 0); - urlbar.dispatchEvent(event); - - SetPageProxyState("invalid"); - } - - Cc["@mozilla.org/widget/clipboardhelper;1"] - .getService(Ci.nsIClipboardHelper) - .copyString(val); - }, - supportsCommand: function(aCommand) { - switch (aCommand) { - case "cmd_copy": - case "cmd_cut": - return true; - } - return false; - }, - isCommandEnabled: function(aCommand) { - return this.supportsCommand(aCommand) && - (aCommand != "cmd_cut" || !this.urlbar.readOnly) && - this.urlbar.selectionStart < this.urlbar.selectionEnd; - }, - onEvent: function(aEventName) {} - }) - ]]></field> - - <method name="observe"> - <parameter name="aSubject"/> - <parameter name="aTopic"/> - <parameter name="aData"/> - <body><![CDATA[ - if (aTopic == "nsPref:changed") { - switch (aData) { - case "clickSelectsAll": - case "doubleClickSelectsAll": - this[aData] = this._prefs.getBoolPref(aData); - break; - case "autoFill": - this.completeDefaultIndex = this._prefs.getBoolPref(aData); - break; - case "delay": - this.timeout = this._prefs.getIntPref(aData); - break; - case "formatting.enabled": - this._formattingEnabled = this._prefs.getBoolPref(aData); - break; - case "suggest.searches": - case "userMadeSearchSuggestionsChoice": - // Mirror the value for future use, see the comment in the - // binding's constructor. - this._prefs.setBoolPref("searchSuggestionsChoice", - this._prefs.getBoolPref("suggest.searches")); - - this._cacheUserMadeSearchSuggestionsChoice(); - if (this._userMadeSearchSuggestionsChoice) { - this.popup.searchSuggestionsNotificationWasDismissed( - this._prefs.getBoolPref("suggest.searches") - ); - } - break; - case "trimURLs": - this._mayTrimURLs = this._prefs.getBoolPref(aData); - break; - case "oneOffSearches": - this._enableOrDisableOneOffSearches(); - break; - } - } - ]]></body> - </method> - - <method name="_enableOrDisableOneOffSearches"> - <body><![CDATA[ - let enable = this._prefs.getBoolPref("oneOffSearches"); - this.popup.enableOneOffSearches(enable); - ]]></body> - </method> - - <method name="handleEvent"> - <parameter name="aEvent"/> - <body><![CDATA[ - switch (aEvent.type) { - case "paste": - let originalPasteData = aEvent.clipboardData.getData("text/plain"); - if (!originalPasteData) { - return; - } - - let oldValue = this.inputField.value; - let oldStart = oldValue.substring(0, this.inputField.selectionStart); - // If there is already non-whitespace content in the URL bar - // preceding the pasted content, it's not necessary to check - // protocols used by the pasted content: - if (oldStart.trim()) { - return; - } - let oldEnd = oldValue.substring(this.inputField.selectionEnd); - - let pasteData = stripUnsafeProtocolOnPaste(originalPasteData); - if (originalPasteData != pasteData) { - // Unfortunately we're not allowed to set the bits being pasted - // so cancel this event: - aEvent.preventDefault(); - aEvent.stopImmediatePropagation(); - - this.inputField.value = oldStart + pasteData + oldEnd; - // Fix up cursor/selection: - let newCursorPos = oldStart.length + pasteData.length; - this.inputField.selectionStart = newCursorPos; - this.inputField.selectionEnd = newCursorPos; - } - break; - case "mousedown": - if (this.doubleClickSelectsAll && - aEvent.button == 0 && aEvent.detail == 2) { - this.editor.selectAll(); - aEvent.preventDefault(); - } - break; - case "mousemove": - this._initURLTooltip(); - break; - case "mouseout": - this._hideURLTooltip(); - break; - case "overflow": - this._contentIsCropped = true; - break; - case "underflow": - this._contentIsCropped = false; - this._hideURLTooltip(); - break; - } - ]]></body> - </method> - - <!-- - onBeforeTextValueSet is called by the base-binding's .textValue getter. - It should return the value that the getter should use. - --> - <method name="onBeforeTextValueGet"> - <body><![CDATA[ - return { value: this.inputField.value }; - ]]></body> - </method> - - <!-- - onBeforeTextValueSet is called by the base-binding's .textValue setter. - It should return the value that the setter should use. - --> - <method name="onBeforeTextValueSet"> - <parameter name="aValue"/> - <body><![CDATA[ - let val = aValue; - let uri; - try { - uri = makeURI(val); - } catch (ex) {} - - if (uri) { - // Do not touch moz-action URIs at all. They depend on being - // properly encoded and decoded and will break if decoded - // unexpectedly. - if (!this._parseActionUrl(val)) { - val = losslessDecodeURI(uri); - } - } - - return val; - ]]></body> - </method> - - <method name="_parseActionUrl"> - <parameter name="aUrl"/> - <body><![CDATA[ - const MOZ_ACTION_REGEX = /^moz-action:([^,]+),(.*)$/; - if (!MOZ_ACTION_REGEX.test(aUrl)) - return null; - - // URL is in the format moz-action:ACTION,PARAMS - // Where PARAMS is a JSON encoded object. - let [, type, params] = aUrl.match(MOZ_ACTION_REGEX); - - let action = { - type: type, - }; - - action.params = JSON.parse(params); - for (let key in action.params) { - action.params[key] = decodeURIComponent(action.params[key]); - } - - if ("url" in action.params) { - let uri; - try { - uri = makeURI(action.params.url); - action.params.displayUrl = losslessDecodeURI(uri); - } catch (e) { - action.params.displayUrl = action.params.url; - } - } - - return action; - ]]></body> - </method> - - <property name="_noActionKeys" readonly="true"> - <getter><![CDATA[ - if (!this.__noActionKeys) { - this.__noActionKeys = new Set([ - KeyEvent.DOM_VK_ALT, - KeyEvent.DOM_VK_SHIFT, - ]); - let modifier = this.AppConstants.platform == "macosx" ? - KeyEvent.DOM_VK_META : - KeyEvent.DOM_VK_CONTROL; - this.__noActionKeys.add(modifier); - } - return this.__noActionKeys; - ]]></getter> - </property> - - <field name="_pressedNoActionKeys"><![CDATA[ - new Set() - ]]></field> - - <method name="_clearNoActions"> - <parameter name="aURL"/> - <body><![CDATA[ - this._pressedNoActionKeys.clear(); - this.popup.removeAttribute("noactions"); - let action = this._parseActionUrl(this._value); - if (action) - this.setAttribute("actiontype", action.type); - ]]></body> - </method> - - <method name="onInput"> - <parameter name="aEvent"/> - <body><![CDATA[ - if (!this.mIgnoreInput && this.mController.input == this) { - this._value = this.inputField.value; - gBrowser.userTypedValue = this.value; - this.valueIsTyped = true; - // Only wait for a result when we are sure to get one. In some - // cases, like when pasting the same exact text, we may not fire - // a new search and we won't get a result. - if (this.mController.handleText()) { - this.gotResultForCurrentQuery = false; - } - } - this.resetActionType(); - ]]></body> - </method> - - <method name="handleEnter"> - <parameter name="event"/> - <body><![CDATA[ - // We need to ensure we're using a selected autocomplete result. - // A result should automatically be selected by default, - // however autocomplete is async and therefore we may not - // have a result set relating to the current input yet. If that - // happens, we need to mark that when the first result does get added, - // it needs to be handled as if enter was pressed with that first - // result selected. - // If anything other than the default (first) result is selected, then - // it must have been manually selected by the human. We let this - // explicit choice be used, even if it may be related to a previous - // input. - // However, if the default result is automatically selected, we - // ensure that it corresponds to the current input. - - // Store the current search string so it can be used in - // handleCommand, which will be called as a result of - // mController.handleEnter(). - // Note this is also used to detect if we should perform a delayed - // handleEnter, in such a case it won't have been cleared. - this.handleEnterInstance = { - searchString: this.mController.searchString, - event: event - }; - - if (this.popup.selectedIndex != 0 || this.gotResultForCurrentQuery) { - this.maybeCanonizeURL(event, this.value); - let rv = this.mController.handleEnter(false, event); - this.handleEnterInstance = null; - this.popup.overrideValue = null; - return rv; - } - - return true; - ]]></body> - </method> - - <method name="handleDelete"> - <body><![CDATA[ - // If the heuristic result is selected, then the autocomplete - // controller's handleDelete implementation will remove it, which is - // not what we want. So in that case, call handleText so it acts as - // a backspace on the text value instead of removing the result. - if (this.popup.selectedIndex == 0 && - this.popup._isFirstResultHeuristic) { - this.mController.handleText(); - return false; - } - return this.mController.handleDelete(); - ]]></body> - </method> - - <field name="_userMadeSearchSuggestionsChoice"><![CDATA[ - false - ]]></field> - - <method name="_cacheUserMadeSearchSuggestionsChoice"> - <body><![CDATA[ - this._userMadeSearchSuggestionsChoice = - this._prefs.getBoolPref("userMadeSearchSuggestionsChoice") || - this._prefs.getBoolPref("suggest.searches"); - ]]></body> - </method> - - <property name="shouldShowSearchSuggestionsNotification" readonly="true"> - <getter><![CDATA[ - return !this._userMadeSearchSuggestionsChoice && - !this.inPrivateContext && - // When _urlbarFocused is true, tabbrowser would close the - // popup if it's opened here, so don't show the notification. - !gBrowser.selectedBrowser._urlbarFocused && - Services.prefs.getBoolPref("browser.search.suggest.enabled") && - this._prefs.getIntPref("daysBeforeHidingSuggestionsPrompt"); - ]]></getter> - </property> - - </implementation> - - <handlers> - <handler event="keydown"><![CDATA[ - if (this._noActionKeys.has(event.keyCode) && - this.popup.selectedIndex >= 0 && - !this._pressedNoActionKeys.has(event.keyCode)) { - if (this._pressedNoActionKeys.size == 0) { - this.popup.setAttribute("noactions", "true"); - this.removeAttribute("actiontype"); - } - this._pressedNoActionKeys.add(event.keyCode); - } - ]]></handler> - - <handler event="keyup"><![CDATA[ - if (this._noActionKeys.has(event.keyCode) && - this._pressedNoActionKeys.has(event.keyCode)) { - this._pressedNoActionKeys.delete(event.keyCode); - if (this._pressedNoActionKeys.size == 0) - this._clearNoActions(); - } - ]]></handler> - - <handler event="focus"><![CDATA[ - if (event.originalTarget == this.inputField) { - this._hideURLTooltip(); - this.formatValue(); - } - ]]></handler> - - <handler event="blur"><![CDATA[ - if (event.originalTarget == this.inputField) { - this._clearNoActions(); - this.formatValue(); - } -#ifdef MOZ_WEBEXTENSIONS - if (ExtensionSearchHandler.hasActiveInputSession()) { - ExtensionSearchHandler.handleInputCancelled(); - } -#endif - ]]></handler> - - <handler event="dragstart" phase="capturing"><![CDATA[ - // Drag only if the gesture starts from the input field. - if (this.inputField != event.originalTarget && - !(this.inputField.compareDocumentPosition(event.originalTarget) & - Node.DOCUMENT_POSITION_CONTAINED_BY)) - return; - - // Drag only if the entire value is selected and it's a valid URI. - var isFullSelection = this.selectionStart == 0 && - this.selectionEnd == this.textLength; - if (!isFullSelection || - this.getAttribute("pageproxystate") != "valid") - return; - - var urlString = gBrowser.selectedBrowser.currentURI.spec; - var title = gBrowser.selectedBrowser.contentTitle || urlString; - var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>"; - - var dt = event.dataTransfer; - dt.setData("text/x-moz-url", urlString + "\n" + title); - dt.setData("text/unicode", urlString); - dt.setData("text/html", htmlString); - - dt.effectAllowed = "copyLink"; - event.stopPropagation(); - ]]></handler> - - <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/> - <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/> - <handler event="select"><![CDATA[ - if (!Cc["@mozilla.org/widget/clipboard;1"] - .getService(Ci.nsIClipboard) - .supportsSelectionClipboard()) - return; - - if (!window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .isHandlingUserInput) - return; - - var val = this._getSelectedValueForClipboard(); - if (!val) - return; - - Cc["@mozilla.org/widget/clipboardhelper;1"] - .getService(Ci.nsIClipboardHelper) - .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard); - ]]></handler> - </handlers> - - </binding> - - <!-- Note: this binding is applied to the autocomplete popup used in web page content and extended in search.xml for the searchbar. --> - <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup"> - <implementation> - <field name="AppConstants" readonly="true"> - (Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants; - </field> - - <method name="openAutocompletePopup"> - <parameter name="aInput"/> - <parameter name="aElement"/> - <body> - <![CDATA[ - // initially the panel is hidden - // to avoid impacting startup / new window performance - aInput.popup.hidden = false; - - // this method is defined on the base binding - this._openAutocompletePopup(aInput, aElement); - ]]></body> - </method> - - <method name="onPopupClick"> - <parameter name="aEvent"/> - <body><![CDATA[ - // Ignore all right-clicks - if (aEvent.button == 2) - return; - - var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); - - var searchBar = BrowserSearch.searchBar; - var popupForSearchBar = searchBar && searchBar.textbox == this.mInput; - if (popupForSearchBar) { - searchBar.telemetrySearchDetails = { - index: controller.selection.currentIndex, - kind: "mouse" - }; - } - - // Check for unmodified left-click, and use default behavior - if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey && - !aEvent.altKey && !aEvent.metaKey) { - controller.handleEnter(true, aEvent); - return; - } - - // Check for middle-click or modified clicks on the search bar - if (popupForSearchBar) { - // Handle search bar popup clicks - var search = controller.getValueAt(this.selectedIndex); - - // open the search results according to the clicking subtlety - var where = whereToOpenLink(aEvent, false, true); - let params = {}; - - // But open ctrl/cmd clicks on autocomplete items in a new background tab. - let modifier = this.AppConstants.platform == "macosx" ? - aEvent.metaKey : - aEvent.ctrlKey; - if (where == "tab" && (aEvent instanceof MouseEvent) && - (aEvent.button == 1 || modifier)) - params.inBackground = true; - - // leave the popup open for background tab loads - if (!(where == "tab" && params.inBackground)) { - // close the autocomplete popup and revert the entered search term - this.closePopup(); - controller.handleEscape(); - } - - searchBar.doSearch(search, where, null, params); - if (where == "tab" && params.inBackground) - searchBar.focus(); - else - searchBar.value = search; - } - ]]></body> - </method> - </implementation> - </binding> - - <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup"> - - <resources> - <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/> - <stylesheet src="chrome://browser/skin/searchbar.css"/> - </resources> - - <content ignorekeys="true" level="top" consumeoutsideclicks="never" - aria-owns="richlistbox"> - <xul:hbox anonid="search-suggestions-notification" - align="center" - role="alert" - aria-describedby="search-suggestions-notification-text"> - <xul:description flex="1"> - &urlbar.searchSuggestionsNotification.question; - <!-- Several things here are to make the label accessibile via an - accesskey so that a11y doesn't suck: the accesskey, using an - onclick handler instead of an href attribute, the control - attribute, and having the control attribute refer to a valid ID - that is the label itself. --> - <xul:label id="search-suggestions-notification-learn-more" - class="text-link" - role="link" - value="&urlbar.searchSuggestionsNotification.learnMore;" - accesskey="&urlbar.searchSuggestionsNotification.learnMore.accesskey;" - onclick="document.getBindingParent(this).openSearchSuggestionsNotificationLearnMoreURL();" - control="search-suggestions-notification-learn-more"/> - </xul:description> - <xul:button anonid="search-suggestions-notification-disable" - label="&urlbar.searchSuggestionsNotification.disable;" - accesskey="&urlbar.searchSuggestionsNotification.disable.accesskey;" - onclick="document.getBindingParent(this).dismissSearchSuggestionsNotification(false);"/> - <xul:button anonid="search-suggestions-notification-enable" - label="&urlbar.searchSuggestionsNotification.enable;" - accesskey="&urlbar.searchSuggestionsNotification.enable.accesskey;" - onclick="document.getBindingParent(this).dismissSearchSuggestionsNotification(true);"/> - </xul:hbox> - <xul:richlistbox anonid="richlistbox" class="autocomplete-richlistbox" - flex="1"/> - <xul:hbox anonid="footer"> - <children/> - <xul:vbox anonid="one-off-search-buttons" - class="search-one-offs" - compact="true" - includecurrentengine="true" - disabletab="true" - flex="1"/> - </xul:hbox> - </content> - - <implementation> - <field name="_maxResults">0</field> - - <field name="_bundle" readonly="true"> - Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle("chrome://browser/locale/places/places.properties"); - </field> - - <field name="searchSuggestionsNotification" readonly="true"> - document.getAnonymousElementByAttribute( - this, "anonid", "search-suggestions-notification" - ); - </field> - - <field name="footer" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", "footer"); - </field> - - <field name="oneOffSearchButtons" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", - "one-off-search-buttons"); - </field> - - <field name="_oneOffSearchesEnabled">false</field> - - <field name="_overrideValue">null</field> - <property name="overrideValue" - onget="return this._overrideValue;" - onset="this._overrideValue = val; return val;"/> - - <method name="onPopupClick"> - <parameter name="aEvent"/> - <body><![CDATA[ - if (aEvent.button == 2) { - // Ignore right-clicks. - return; - } - // Otherwise "call super" -- do what autocomplete-base-popup does. - let controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController); - controller.handleEnter(true, aEvent); - ]]></body> - </method> - - <method name="enableOneOffSearches"> - <parameter name="enable"/> - <body><![CDATA[ - this._oneOffSearchesEnabled = enable; - if (enable) { - this.oneOffSearchButtons.telemetryOrigin = "urlbar"; - this.oneOffSearchButtons.style.display = "-moz-box"; - this.oneOffSearchButtons.popup = this; - this.oneOffSearchButtons.textbox = this.input; - } else { - this.oneOffSearchButtons.telemetryOrigin = null; - this.oneOffSearchButtons.style.display = "none"; - this.oneOffSearchButtons.popup = null; - this.oneOffSearchButtons.textbox = null; - } - ]]></body> - </method> - - <method name="openSearchSuggestionsNotificationLearnMoreURL"> - <body><![CDATA[ - let url = Services.urlFormatter.formatURL( - Services.prefs.getCharPref("app.support.baseURL") + "suggestions" - ); - openUILinkIn(url, "tab"); - ]]></body> - </method> - - <method name="dismissSearchSuggestionsNotification"> - <parameter name="enableSuggestions"/> - <body><![CDATA[ - // Make sure the urlbar is focused. It won't be, for example, if the - // user used an accesskey to make an opt-in choice. mIgnoreFocus - // prevents the text from being selected. - this.input.mIgnoreFocus = true; - this.input.focus(); - this.input.mIgnoreFocus = false; - - Services.prefs.setBoolPref( - "browser.urlbar.suggest.searches", enableSuggestions - ); - Services.prefs.setBoolPref( - "browser.urlbar.userMadeSearchSuggestionsChoice", true - ); - // The input's pref observer will now hide the notification. - ]]></body> - </method> - - <!-- Override this so that navigating between items results in an item - always being selected. --> - <method name="getNextIndex"> - <parameter name="reverse"/> - <parameter name="amount"/> - <parameter name="index"/> - <parameter name="maxRow"/> - <body><![CDATA[ - if (maxRow < 0) - return -1; - - let newIndex = index + (reverse ? -1 : 1) * amount; - - // We only want to wrap if navigation is in any direction by one item, - // otherwise we clamp to one end of the list. - // ie, hitting page-down will only cause is to wrap if we're already - // at one end of the list. - - // Allow the selection to be removed if the first result is not a - // heuristic result. - if (!this._isFirstResultHeuristic) { - if (reverse && index == -1 || newIndex > maxRow && index != maxRow) - newIndex = maxRow; - else if (!reverse && index == -1 || newIndex < 0 && index != 0) - newIndex = 0; - - if (newIndex < 0 && index == 0 || newIndex > maxRow && index == maxRow) - newIndex = -1; - - return newIndex; - } - - // Otherwise do not allow the selection to be removed. - if (newIndex < 0) { - newIndex = index > 0 ? 0 : maxRow; - } else if (newIndex > maxRow) { - newIndex = index < maxRow ? maxRow : 0; - } - return newIndex; - ]]></body> - </method> - - <property name="_isFirstResultHeuristic" readonly="true"> - <getter> - <![CDATA[ - // The popup usually has a special "heuristic" first result (added - // by UnifiedComplete.js) that is automatically selected when the - // popup opens. - return this.input.mController.matchCount > 0 && - this.input.mController - .getStyleAt(0) - .split(/\s+/).indexOf("heuristic") > 0; - ]]> - </getter> - </property> - - <property name="maxResults" readonly="true"> - <getter> - <![CDATA[ - if (!this._maxResults) { - var prefService = - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - this._maxResults = prefService.getIntPref("browser.urlbar.maxRichResults"); - } - return this._maxResults; - ]]> - </getter> - </property> - - <method name="openAutocompletePopup"> - <parameter name="aInput"/> - <parameter name="aElement"/> - <body> - <![CDATA[ - // initially the panel is hidden - // to avoid impacting startup / new window performance - aInput.popup.hidden = false; - - let showNotification = aInput.shouldShowSearchSuggestionsNotification; - if (showNotification) { - let prefs = aInput._prefs; - let now = new Date(); - let date = now.getFullYear() * 10000 + (now.getMonth() + 1) * 100 + now.getDate(); - let previousDate = prefs.getIntPref("lastSuggestionsPromptDate"); - if (previousDate < date) { - let remainingDays = - prefs.getIntPref("daysBeforeHidingSuggestionsPrompt") - 1; - prefs.setIntPref("daysBeforeHidingSuggestionsPrompt", - remainingDays); - prefs.setIntPref("lastSuggestionsPromptDate", date); - if (!remainingDays) - showNotification = false; - } - } - - if (showNotification) { - this._showSearchSuggestionsNotification(); - } else if (this.classList.contains("showSearchSuggestionsNotification")) { - this._hideSearchSuggestionsNotification(); - } - - this._openAutocompletePopup(aInput, aElement); - ]]> - </body> - </method> - - <method name="_openAutocompletePopup"> - <parameter name="aInput"/> - <parameter name="aElement"/> - <body><![CDATA[ - if (this.mPopupOpen) { - return; - } - - this.mInput = aInput; - aInput.controller.setInitiallySelectedIndex(this._isFirstResultHeuristic ? 0 : -1); - this.view = aInput.controller.QueryInterface(Components.interfaces.nsITreeView); - this._invalidate(); - - var rect = window.document.documentElement.getBoundingClientRect(); - var width = rect.right - rect.left; - this.setAttribute("width", width); - - // Adjust the direction of the autocomplete popup list based on the textbox direction, bug 649840 - var popupDirection = aElement.ownerDocument.defaultView.getComputedStyle(aElement).direction; - this.style.direction = popupDirection; - - // Make the popup's starting margin negative so that the leading edge - // of the popup aligns with the window border. - let elementRect = aElement.getBoundingClientRect(); - if (popupDirection == "rtl") { - let offset = elementRect.right - rect.right - this.style.marginRight = offset + "px"; - } else { - let offset = rect.left - elementRect.left; - this.style.marginLeft = offset + "px"; - } - - // Keep the popup items' site icons aligned with the urlbar's identity - // icon if it's not too far from the edge of the window. If there are - // at most two toolbar buttons between the window edge and the urlbar, - // then consider that as "not too far." The forward button's - // visibility may have changed since the last time the popup was - // opened, so this needs to happen now. Do it *before* the popup - // opens because otherwise the items will visibly shift. - let nodes = [...document.getElementById("nav-bar-customization-target").childNodes]; - let urlbarPosition = nodes.findIndex(n => n.id == "urlbar-container"); - let alignSiteIcons = urlbarPosition <= 2 && - nodes.slice(0, urlbarPosition) - .every(n => n.localName == "toolbarbutton"); - if (alignSiteIcons) { - let identityRect = - document.getElementById("identity-icon").getBoundingClientRect(); - this.siteIconStart = popupDirection == "rtl" ? identityRect.right - : identityRect.left; - } - else { - // Reset the alignment so that the site icons are positioned - // according to whatever's in the CSS. - this.siteIconStart = undefined; - } - - // Position the popup below the navbar. To get the y-coordinate, - // which is an offset from the bottom of the input, subtract the - // bottom of the navbar from the buttom of the input. - let yOffset = - document.getElementById("nav-bar").getBoundingClientRect().bottom - - aInput.getBoundingClientRect().bottom; - this.openPopup(aElement, "after_start", 0, yOffset, false, false); - ]]></body> - </method> - - <method name="_updateFooterVisibility"> - <body> - <![CDATA[ - this.footer.collapsed = this._matchCount == 0; - ]]> - </body> - </method> - - <method name="_showSearchSuggestionsNotification"> - <body> - <![CDATA[ - // With the notification shown, the listbox's height can sometimes be - // too small when it's flexed, as it normally is. Also, it can start - // out slightly scrolled down. Both problems appear together, most - // often when the popup is very narrow and the notification's text - // must wrap. Work around them by removing the flex. - // - // But without flexing the listbox, the listbox's height animation - // sometimes fails to complete, leaving the popup too tall. Work - // around that problem by disabling the listbox animation. - this.richlistbox.flex = 0; - this.setAttribute("dontanimate", "true"); - - this.classList.add("showSearchSuggestionsNotification"); - this._updateFooterVisibility(); - - // This event allows accessibility APIs to see the notification. - if (!this.popupOpen) { - let event = document.createEvent("Events"); - event.initEvent("AlertActive", true, true); - this.searchSuggestionsNotification.dispatchEvent(event); - } - ]]> - </body> - </method> - - <method name="searchSuggestionsNotificationWasDismissed"> - <parameter name="enableSuggestions"/> - <body> - <![CDATA[ - if (!this.popupOpen) { - this._hideSearchSuggestionsNotification(); - return; - } - this._hideSearchSuggestionsNotificationWithAnimation().then(() => { - if (enableSuggestions && this.input.textValue) { - // Start a new search so that suggestions appear immediately. - this.input.controller.startSearch(this.input.textValue); - } - }); - ]]> - </body> - </method> - - <method name="_hideSearchSuggestionsNotification"> - <body> - <![CDATA[ - this.classList.remove("showSearchSuggestionsNotification"); - this.richlistbox.flex = 1; - this.removeAttribute("dontanimate"); - if (this._matchCount) { - // Update popup height. - this._invalidate(); - } else { - this.closePopup(); - } - ]]> - </body> - </method> - - <method name="_hideSearchSuggestionsNotificationWithAnimation"> - <body> - <![CDATA[ - return new Promise(resolve => { - let notificationHeight = this.searchSuggestionsNotification - .getBoundingClientRect() - .height; - this.searchSuggestionsNotification.style.marginTop = - "-" + notificationHeight + "px"; - - let popupHeightPx = - (this.getBoundingClientRect().height - notificationHeight) + "px"; - this.style.height = popupHeightPx; - - let onTransitionEnd = () => { - this.removeEventListener("transitionend", onTransitionEnd, true); - this.searchSuggestionsNotification.style.marginTop = "0px"; - this.style.removeProperty("height"); - this._hideSearchSuggestionsNotification(); - resolve(); - }; - this.addEventListener("transitionend", onTransitionEnd, true); - }); - ]]> - </body> - </method> - - <method name="_selectedOneOffChanged"> - <body><![CDATA[ - // Update all searchengine result items to use the newly selected - // engine. - for (let item of this.richlistbox.childNodes) { - if (item.collapsed) { - break; - } - let url = item.getAttribute("url"); - if (url) { - let action = item._parseActionUrl(url); - if (action && action.type == "searchengine") { - item._adjustAcItem(); - } - } - } - ]]></body> - </method> - - <!-- This handles keypress changes to the selection among the one-off - search buttons and between the one-offs and the listbox. It returns - true if the keypress was consumed and false if not. --> - <method name="handleKeyPress"> - <parameter name="aEvent"/> - <body><![CDATA[ - this.oneOffSearchButtons.handleKeyPress(aEvent, this._matchCount, - !this._isFirstResultHeuristic, - gBrowser.userTypedValue); - return aEvent.defaultPrevented; - ]]></body> - </method> - - <!-- This is called when a one-off is clicked and when "search in new tab" - is selected from a one-off context menu. --> - <method name="handleOneOffSearch"> - <parameter name="event"/> - <parameter name="engine"/> - <parameter name="where"/> - <parameter name="params"/> - <body><![CDATA[ - this.input.handleCommand(event, where, params); - ]]></body> - </method> - - <!-- Result listitems call this to determine which search engine they - should show in their labels and include in their url attributes. --> - <property name="overrideSearchEngineName" readonly="true"> - <getter><![CDATA[ - let button = this.oneOffSearchButtons.selectedButton; - return button && button.engine && button.engine.name; - ]]></getter> - </property> - - <method name="createResultLabel"> - <parameter name="item"/> - <parameter name="proposedLabel"/> - <body> - <![CDATA[ - let parts = [proposedLabel]; - - let action = this.mInput._parseActionUrl(item.getAttribute("url")); - if (action) { - switch (action.type) { - case "searchengine": - parts = [ - action.params.searchSuggestion || action.params.searchQuery, - action.params.engineName, - ]; - break; - case "switchtab": - case "remotetab": - parts = [ - item.getAttribute("title"), - item.getAttribute("displayurl"), - ]; - break; - } - } - - let types = item.getAttribute("type").split(/\s+/); - let type = types.find(type => type != "action" && type != "heuristic"); - try { - // Some types intentionally do not map to strings, which is not - // an error. - parts.push(this._bundle.GetStringFromName(type + "ResultLabel")); - } catch (e) {} - - return parts.filter(str => str).join(" "); - ]]> - </body> - </method> - - <method name="onResultsAdded"> - <body> - <![CDATA[ - // If nothing is selected yet, select the first result if it is a - // pre-selected "heuristic" result. (See UnifiedComplete.js.) - if (this.selectedIndex == -1 && this._isFirstResultHeuristic) { - // Don't fire DOMMenuItemActive so that screen readers still see - // the input as being focused. - this.richlistbox.suppressMenuItemEvent = true; - this.input.controller.setInitiallySelectedIndex(0); - this.richlistbox.suppressMenuItemEvent = false; - } - - this.input.gotResultForCurrentQuery = true; - - // Check if we should perform a delayed handleEnter. - if (this.input.handleEnterInstance) { - let instance = this.input.handleEnterInstance; - this.input.handleEnterInstance = null; - // Don't handle this immediately or we could cause a recursive - // loop where the controller sets popupOpen and re-enters here. - setTimeout(() => { - // Safety check: handle only if the search string didn't change. - let { event, searchString } = instance; - if (this.input.mController.searchString == searchString) { - this.input.maybeCanonizeURL(event, searchString); - this.input.mController.handleEnter(false, event); - this.overrideValue = null; - } - }, 0); - } - ]]> - </body> - </method> - - <method name="_onSearchBegin"> - <body><![CDATA[ - // Set the selected index to 0 (heuristic) until a result comes back - // and we can evaluate it better. - // - // This is required to properly manage delayed handleEnter: - // 1. if a search starts we set selectedIndex to 0 here, and it will - // be updated by onResultsAdded. Since selectedIndex is 0, - // handleEnter will delay the action if a result didn't arrive yet. - // 2. if a search doesn't start (for example if autocomplete is - // disabled), this won't be called, and the selectedIndex will be - // the default -1 value. Then handleEnter will know it should not - // delay the action, cause a result wont't ever arrive. - this.input.controller.setInitiallySelectedIndex(0); - ]]></body> - </method> - - </implementation> - <handlers> - - <handler event="SelectedOneOffButtonChanged"><![CDATA[ - this._selectedOneOffChanged(); - ]]></handler> - - <handler event="mousedown"><![CDATA[ - // Required to make the xul:label.text-link elements in the search - // suggestions notification work correctly when clicked on Linux. - // This is copied from the mousedown handler in - // browser-search-autocomplete-result-popup, which apparently had a - // similar problem. - event.preventDefault(); - ]]></handler> - - </handlers> - </binding> - - <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> - <implementation> - <constructor><![CDATA[ - if (!this.notification) - return; - - this.notification.options.installs.forEach(function(aInstall) { - aInstall.addListener(this); - }, this); - - // Calling updateProgress can sometimes cause this notification to be - // removed in the middle of refreshing the notification panel which - // makes the panel get refreshed again. Just initialise to the - // undetermined state and then schedule a proper check at the next - // opportunity - this.setProgress(0, -1); - this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0); - ]]></constructor> - - <destructor><![CDATA[ - this.destroy(); - ]]></destructor> - - <field name="progressmeter" readonly="true"> - document.getElementById("addon-progress-notification-progressmeter"); - </field> - <field name="progresstext" readonly="true"> - document.getElementById("addon-progress-notification-progresstext"); - </field> - <property name="DownloadUtils" readonly="true"> - <getter><![CDATA[ - let module = {}; - Components.utils.import("resource://gre/modules/DownloadUtils.jsm", module); - Object.defineProperty(this, "DownloadUtils", { - configurable: true, - enumerable: true, - writable: true, - value: module.DownloadUtils - }); - return module.DownloadUtils; - ]]></getter> - </property> - - <method name="destroy"> - <body><![CDATA[ - if (!this.notification) - return; - - this.notification.options.installs.forEach(function(aInstall) { - aInstall.removeListener(this); - }, this); - clearTimeout(this._updateProgressTimeout); - ]]></body> - </method> - - <method name="setProgress"> - <parameter name="aProgress"/> - <parameter name="aMaxProgress"/> - <body><![CDATA[ - if (aMaxProgress == -1) { - this.progressmeter.setAttribute("mode", "undetermined"); - } - else { - this.progressmeter.setAttribute("mode", "determined"); - this.progressmeter.setAttribute("value", (aProgress * 100) / aMaxProgress); - } - - let now = Date.now(); - - if (!this.notification.lastUpdate) { - this.notification.lastUpdate = now; - this.notification.lastProgress = aProgress; - return; - } - - let delta = now - this.notification.lastUpdate; - if ((delta < 400) && (aProgress < aMaxProgress)) - return; - - delta /= 1000; - - // This code is taken from nsDownloadManager.cpp - let speed = (aProgress - this.notification.lastProgress) / delta; - if (this.notification.speed) - speed = speed * 0.9 + this.notification.speed * 0.1; - - this.notification.lastUpdate = now; - this.notification.lastProgress = aProgress; - this.notification.speed = speed; - - let status = null; - [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last); - this.progresstext.setAttribute("value", status); - this.progresstext.setAttribute("tooltiptext", status); - ]]></body> - </method> - - <method name="cancel"> - <body><![CDATA[ - let installs = this.notification.options.installs; - installs.forEach(function(aInstall) { - try { - aInstall.cancel(); - } - catch (e) { - // Cancel will throw if the download has already failed - } - }, this); - - PopupNotifications.remove(this.notification); - ]]></body> - </method> - - <method name="updateProgress"> - <body><![CDATA[ - if (!this.notification) - return; - - let downloadingCount = 0; - let progress = 0; - let maxProgress = 0; - - this.notification.options.installs.forEach(function(aInstall) { - if (aInstall.maxProgress == -1) - maxProgress = -1; - progress += aInstall.progress; - if (maxProgress >= 0) - maxProgress += aInstall.maxProgress; - if (aInstall.state < AddonManager.STATE_DOWNLOADED) - downloadingCount++; - }); - - if (downloadingCount == 0) { - this.destroy(); - if (Preferences.get("xpinstall.customConfirmationUI", false)) { - this.progressmeter.setAttribute("mode", "undetermined"); - let status = gNavigatorBundle.getString("addonDownloadVerifying"); - this.progresstext.setAttribute("value", status); - this.progresstext.setAttribute("tooltiptext", status); - } else { - PopupNotifications.remove(this.notification); - } - } - else { - this.setProgress(progress, maxProgress); - } - ]]></body> - </method> - - <method name="onDownloadProgress"> - <body><![CDATA[ - this.updateProgress(); - ]]></body> - </method> - - <method name="onDownloadFailed"> - <body><![CDATA[ - this.updateProgress(); - ]]></body> - </method> - - <method name="onDownloadCancelled"> - <body><![CDATA[ - this.updateProgress(); - ]]></body> - </method> - - <method name="onDownloadEnded"> - <body><![CDATA[ - this.updateProgress(); - ]]></body> - </method> - </implementation> - </binding> - - <binding id="plugin-popupnotification-center-item"> - <content align="center"> - <xul:vbox pack="center" anonid="itemBox" class="itemBox"> - <xul:description anonid="center-item-label" class="center-item-label" /> - <xul:hbox flex="1" pack="start" align="center" anonid="center-item-warning"> - <xul:image anonid="center-item-warning-icon" class="center-item-warning-icon"/> - <xul:label anonid="center-item-warning-label"/> - <xul:label anonid="center-item-link" value="&checkForUpdates;" class="text-link"/> - </xul:hbox> - </xul:vbox> - <xul:vbox pack="center"> - <xul:menulist class="center-item-menulist" - anonid="center-item-menulist"> - <xul:menupopup> - <xul:menuitem anonid="allownow" value="allownow" - label="&pluginActivateNow.label;" /> - <xul:menuitem anonid="allowalways" value="allowalways" - label="&pluginActivateAlways.label;" /> - <xul:menuitem anonid="block" value="block" - label="&pluginBlockNow.label;" /> - </xul:menupopup> - </xul:menulist> - </xul:vbox> - </content> - <resources> - <stylesheet src="chrome://global/skin/notification.css"/> - </resources> - <implementation> - <constructor><![CDATA[ - document.getAnonymousElementByAttribute(this, "anonid", "center-item-label").value = this.action.pluginName; - - let curState = "block"; - if (this.action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { - if (this.action.pluginPermissionType == Ci.nsIPermissionManager.EXPIRE_SESSION) { - curState = "allownow"; - } - else { - curState = "allowalways"; - } - } - document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").value = curState; - - let warningString = ""; - let linkString = ""; - - let link = document.getAnonymousElementByAttribute(this, "anonid", "center-item-link"); - - let url; - let linkHandler; - - if (this.action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { - document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; - warningString = gNavigatorBundle.getString("pluginActivateDisabled.label"); - linkString = gNavigatorBundle.getString("pluginActivateDisabled.manage"); - linkHandler = function(event) { - event.preventDefault(); - gPluginHandler.managePlugins(); - }; - document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-icon").hidden = true; - } - else { - url = this.action.detailsLink; - - switch (this.action.blocklistState) { - case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: - document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning").hidden = true; - break; - case Ci.nsIBlocklistService.STATE_BLOCKED: - document.getAnonymousElementByAttribute(this, "anonid", "center-item-menulist").hidden = true; - warningString = gNavigatorBundle.getString("pluginActivateBlocked.label"); - linkString = gNavigatorBundle.getString("pluginActivate.learnMore"); - break; - case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: - warningString = gNavigatorBundle.getString("pluginActivateOutdated.label"); - linkString = gNavigatorBundle.getString("pluginActivate.updateLabel"); - break; - case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: - warningString = gNavigatorBundle.getString("pluginActivateVulnerable.label"); - linkString = gNavigatorBundle.getString("pluginActivate.riskLabel"); - break; - } - } - document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString; - - let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow); - let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin); - - if (isWindowPrivate) { - // TODO: temporary compromise of hiding some privacy leaks, remove once bug 892487 is fixed - let allowalways = document.getAnonymousElementByAttribute(this, "anonid", "allowalways"); - let block = document.getAnonymousElementByAttribute(this, "anonid", "block"); - let allownow = document.getAnonymousElementByAttribute(this, "anonid", "allownow"); - - allowalways.hidden = curState !== "allowalways"; - block.hidden = curState !== "block"; - allownow.hidden = curState === "allowalways"; - } - - if (url || linkHandler) { - link.value = linkString; - if (url) { - link.href = url; - } - if (linkHandler) { - link.addEventListener("click", linkHandler, false); - } - } - else { - link.hidden = true; - } - ]]></constructor> - <property name="value"> - <getter> - return document.getAnonymousElementByAttribute(this, "anonid", - "center-item-menulist").value; - </getter> - <setter><!-- This should be used only in automated tests --> - document.getAnonymousElementByAttribute(this, "anonid", - "center-item-menulist").value = val; - </setter> - </property> - </implementation> - </binding> - - <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification"> - <content align="start" style="width: &pluginNotification.width;;"> - <xul:vbox flex="1" align="stretch" class="popup-notification-main-box" - xbl:inherits="popupid"> - <xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start"> - <xul:description class="click-to-play-plugins-outer-description" flex="1"> - <html:span anonid="click-to-play-plugins-notification-description" /> - <xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" /> - </xul:description> - <xul:toolbarbutton anonid="closebutton" - class="messageCloseButton popup-notification-closebutton tabbable close-icon" - xbl:inherits="oncommand=closebuttoncommand" - tooltiptext="&closeNotification.tooltip;"/> - </xul:hbox> - <xul:grid anonid="click-to-play-plugins-notification-center-box" - class="click-to-play-plugins-notification-center-box"> - <xul:columns> - <xul:column flex="1"/> - <xul:column/> - </xul:columns> - <xul:rows> - <children includes="row"/> - <xul:hbox pack="start" anonid="plugin-notification-showbox"> - <xul:button label="&pluginNotification.showAll.label;" - accesskey="&pluginNotification.showAll.accesskey;" - class="plugin-notification-showbutton" - oncommand="document.getBindingParent(this)._setState(2)"/> - </xul:hbox> - </xul:rows> - </xul:grid> - <xul:hbox anonid="button-container" - class="click-to-play-plugins-notification-button-container" - pack="center" align="center"> - <xul:button anonid="primarybutton" - class="click-to-play-popup-button" - oncommand="document.getBindingParent(this)._onButton(this)" - flex="1"/> - <xul:button anonid="secondarybutton" - class="click-to-play-popup-button" - oncommand="document.getBindingParent(this)._onButton(this);" - flex="1"/> - </xul:hbox> - <xul:box hidden="true"> - <children/> - </xul:box> - </xul:vbox> - </content> - <resources> - <stylesheet src="chrome://global/skin/notification.css"/> - </resources> - <implementation> - <field name="_states"> - ({SINGLE: 0, MULTI_COLLAPSED: 1, MULTI_EXPANDED: 2}) - </field> - <field name="_primaryButton"> - document.getAnonymousElementByAttribute(this, "anonid", "primarybutton"); - </field> - <field name="_secondaryButton"> - document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton") - </field> - <field name="_buttonContainer"> - document.getAnonymousElementByAttribute(this, "anonid", "button-container") - </field> - <field name="_brandShortName"> - document.getElementById("bundle_brand").getString("brandShortName") - </field> - <field name="_items">[]</field> - <constructor><![CDATA[ - const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let sortedActions = []; - for (let action of this.notification.options.pluginData.values()) { - sortedActions.push(action); - } - sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName)); - - for (let action of sortedActions) { - let item = document.createElementNS(XUL_NS, "row"); - item.setAttribute("class", "plugin-popupnotification-centeritem"); - item.action = action; - this.appendChild(item); - this._items.push(item); - } - switch (this._items.length) { - case 0: - PopupNotifications._dismiss(); - break; - case 1: - this._setState(this._states.SINGLE); - break; - default: - if (this.notification.options.primaryPlugin) { - this._setState(this._states.MULTI_COLLAPSED); - } else { - this._setState(this._states.MULTI_EXPANDED); - } - } - ]]></constructor> - <method name="_setState"> - <parameter name="state" /> - <body><![CDATA[ - var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box"); - - if (this._states.SINGLE == state) { - grid.hidden = true; - this._setupSingleState(); - return; - } - - let prePath = this.notification.options.principal.URI.prePath; - this._setupDescription("pluginActivateMultiple.message", null, prePath); - - var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox"); - - var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties"); - this._primaryButton.label = dialogStrings.GetStringFromName("button-accept"); - this._primaryButton.setAttribute("default", "true"); - - this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel"); - this._primaryButton.setAttribute("action", "_multiAccept"); - this._secondaryButton.setAttribute("action", "_cancel"); - - grid.hidden = false; - - if (this._states.MULTI_COLLAPSED == state) { - for (let child of this.childNodes) { - if (child.tagName != "row") { - continue; - } - child.hidden = this.notification.options.primaryPlugin != - child.action.permissionString; - } - showBox.hidden = false; - } - else { - for (let child of this.childNodes) { - if (child.tagName != "row") { - continue; - } - child.hidden = false; - } - showBox.hidden = true; - } - this._setupLink(null); - ]]></body> - </method> - <method name="_setupSingleState"> - <body><![CDATA[ - var action = this._items[0].action; - var prePath = action.pluginPermissionPrePath; - let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow); - let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin); - - let label, linkLabel, button1, button2; - - if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) { - button1 = { - label: "pluginBlockNow.label", - accesskey: "pluginBlockNow.accesskey", - action: "_singleBlock" - }; - button2 = { - label: "pluginContinue.label", - accesskey: "pluginContinue.accesskey", - action: "_singleContinue", - default: true - }; - switch (action.blocklistState) { - case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: - label = "pluginEnabled.message"; - linkLabel = "pluginActivate.learnMore"; - break; - - case Ci.nsIBlocklistService.STATE_BLOCKED: - Cu.reportError(Error("Cannot happen!")); - break; - - case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: - label = "pluginEnabledOutdated.message"; - linkLabel = "pluginActivate.updateLabel"; - break; - - case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: - label = "pluginEnabledVulnerable.message"; - linkLabel = "pluginActivate.riskLabel" - break; - - default: - Cu.reportError(Error("Unexpected blocklist state")); - } - - // TODO: temporary compromise, remove this once bug 892487 is fixed - if (isWindowPrivate) { - this._buttonContainer.hidden = true; - } - } - else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) { - let linkElement = - document.getAnonymousElementByAttribute( - this, "anonid", "click-to-play-plugins-notification-link"); - linkElement.textContent = gNavigatorBundle.getString("pluginActivateDisabled.manage"); - linkElement.setAttribute("onclick", "gPluginHandler.managePlugins()"); - - let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); - descElement.textContent = gNavigatorBundle.getFormattedString( - "pluginActivateDisabled.message", [action.pluginName, this._brandShortName]) + " "; - this._buttonContainer.hidden = true; - return; - } - else if (action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { - let descElement = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); - descElement.textContent = gNavigatorBundle.getFormattedString( - "pluginActivateBlocked.message", [action.pluginName, this._brandShortName]) + " "; - this._setupLink("pluginActivate.learnMore", action.detailsLink); - this._buttonContainer.hidden = true; - return; - } - else { - button1 = { - label: "pluginActivateNow.label", - accesskey: "pluginActivateNow.accesskey", - action: "_singleActivateNow" - }; - button2 = { - label: "pluginActivateAlways.label", - accesskey: "pluginActivateAlways.accesskey", - action: "_singleActivateAlways" - }; - switch (action.blocklistState) { - case Ci.nsIBlocklistService.STATE_NOT_BLOCKED: - label = "pluginActivateNew.message"; - linkLabel = "pluginActivate.learnMore"; - button2.default = true; - break; - - case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE: - label = "pluginActivateOutdated.message"; - linkLabel = "pluginActivate.updateLabel"; - button1.default = true; - break; - - case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE: - label = "pluginActivateVulnerable.message"; - linkLabel = "pluginActivate.riskLabel" - button1.default = true; - break; - - default: - Cu.reportError(Error("Unexpected blocklist state")); - } - - // TODO: temporary compromise, remove this once bug 892487 is fixed - if (isWindowPrivate) { - button1.default = true; - this._secondaryButton.hidden = true; - } - } - this._setupDescription(label, action.pluginName, prePath); - this._setupLink(linkLabel, action.detailsLink); - - this._primaryButton.label = gNavigatorBundle.getString(button1.label); - this._primaryButton.accessKey = gNavigatorBundle.getString(button1.accesskey); - this._primaryButton.setAttribute("action", button1.action); - - this._secondaryButton.label = gNavigatorBundle.getString(button2.label); - this._secondaryButton.accessKey = gNavigatorBundle.getString(button2.accesskey); - this._secondaryButton.setAttribute("action", button2.action); - if (button1.default) { - this._primaryButton.setAttribute("default", "true"); - } - else if (button2.default) { - this._secondaryButton.setAttribute("default", "true"); - } - ]]></body> - </method> - <method name="_setupDescription"> - <parameter name="baseString" /> - <parameter name="pluginName" /> <!-- null for the multiple-plugin case --> - <parameter name="prePath" /> - <body><![CDATA[ - var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description"); - while (span.lastChild) { - span.removeChild(span.lastChild); - } - - var args = ["__prepath__", this._brandShortName]; - if (pluginName) { - args.unshift(pluginName); - } - var bases = gNavigatorBundle.getFormattedString(baseString, args). - split("__prepath__", 2); - - span.appendChild(document.createTextNode(bases[0])); - var prePathSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em"); - prePathSpan.appendChild(document.createTextNode(prePath)); - span.appendChild(prePathSpan); - span.appendChild(document.createTextNode(bases[1] + " ")); - ]]></body> - </method> - <method name="_setupLink"> - <parameter name="linkString"/> - <parameter name="linkUrl" /> - <body><![CDATA[ - var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link"); - if (!linkString || !linkUrl) { - link.hidden = true; - return; - } - - link.hidden = false; - link.textContent = gNavigatorBundle.getString(linkString); - link.href = linkUrl; - ]]></body> - </method> - <method name="_onButton"> - <parameter name="aButton" /> - <body><![CDATA[ - let methodName = aButton.getAttribute("action"); - this[methodName](); - ]]></body> - </method> - <method name="_singleActivateNow"> - <body><![CDATA[ - gPluginHandler._updatePluginPermission(this.notification, - this._items[0].action, - "allownow"); - this._cancel(); - ]]></body> - </method> - <method name="_singleBlock"> - <body><![CDATA[ - gPluginHandler._updatePluginPermission(this.notification, - this._items[0].action, - "block"); - this._cancel(); - ]]></body> - </method> - <method name="_singleActivateAlways"> - <body><![CDATA[ - gPluginHandler._updatePluginPermission(this.notification, - this._items[0].action, - "allowalways"); - this._cancel(); - ]]></body> - </method> - <method name="_singleContinue"> - <body><![CDATA[ - gPluginHandler._updatePluginPermission(this.notification, - this._items[0].action, - "continue"); - this._cancel(); - ]]></body> - </method> - <method name="_multiAccept"> - <body><![CDATA[ - for (let item of this._items) { - let action = item.action; - if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED || - action.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { - continue; - } - gPluginHandler._updatePluginPermission(this.notification, - item.action, item.value); - } - this._cancel(); - ]]></body> - </method> - <method name="_cancel"> - <body><![CDATA[ - PopupNotifications._dismiss(); - ]]></body> - </method> - <method name="_accept"> - <parameter name="aEvent" /> - <body><![CDATA[ - if (aEvent.defaultPrevented) - return; - aEvent.preventDefault(); - if (this._primaryButton.getAttribute("default") == "true") { - this._primaryButton.click(); - } - else if (this._secondaryButton.getAttribute("default") == "true") { - this._secondaryButton.click(); - } - ]]></body> - </method> - </implementation> - <handlers> - <!-- The _accept method checks for .defaultPrevented so that if focus is in a button, - enter activates the button and not this default action --> - <handler event="keypress" keycode="VK_RETURN" group="system" action="this._accept(event);"/> - </handlers> - </binding> - - <binding id="splitmenu"> - <content> - <xul:hbox anonid="menuitem" flex="1" - class="splitmenu-menuitem" - xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/> - <xul:menu anonid="menu" class="splitmenu-menu" - xbl:inherits="disabled,_moz-menuactive=active" - oncommand="event.stopPropagation();"> - <children includes="menupopup"/> - </xul:menu> - </content> - - <implementation implements="nsIDOMEventListener"> - <constructor><![CDATA[ - this._parentMenupopup.addEventListener("DOMMenuItemActive", this, false); - this._parentMenupopup.addEventListener("popuphidden", this, false); - ]]></constructor> - - <destructor><![CDATA[ - this._parentMenupopup.removeEventListener("DOMMenuItemActive", this, false); - this._parentMenupopup.removeEventListener("popuphidden", this, false); - ]]></destructor> - - <field name="menuitem" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", "menuitem"); - </field> - <field name="menu" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", "menu"); - </field> - - <field name="_menuDelay">600</field> - - <field name="_parentMenupopup"><![CDATA[ - this._getParentMenupopup(this); - ]]></field> - - <method name="_getParentMenupopup"> - <parameter name="aNode"/> - <body><![CDATA[ - let node = aNode.parentNode; - while (node) { - if (node.localName == "menupopup") - break; - node = node.parentNode; - } - return node; - ]]></body> - </method> - - <method name="handleEvent"> - <parameter name="event"/> - <body><![CDATA[ - switch (event.type) { - case "DOMMenuItemActive": - if (this.getAttribute("active") == "true" && - event.target != this && - this._getParentMenupopup(event.target) == this._parentMenupopup) - this.removeAttribute("active"); - break; - case "popuphidden": - if (event.target == this._parentMenupopup) - this.removeAttribute("active"); - break; - } - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="mouseover"><![CDATA[ - if (this.getAttribute("active") != "true") { - this.setAttribute("active", "true"); - - let event = document.createEvent("Events"); - event.initEvent("DOMMenuItemActive", true, false); - this.dispatchEvent(event); - - if (this.getAttribute("disabled") != "true") { - let self = this; - setTimeout(function () { - if (self.getAttribute("active") == "true") - self.menu.open = true; - }, this._menuDelay); - } - } - ]]></handler> - - <handler event="popupshowing"><![CDATA[ - if (event.target == this.firstChild && - this._parentMenupopup._currentPopup) - this._parentMenupopup._currentPopup.hidePopup(); - ]]></handler> - - <handler event="click" phase="capturing"><![CDATA[ - if (this.getAttribute("disabled") == "true") { - // Prevent the command from being carried out - event.stopPropagation(); - return; - } - - let node = event.originalTarget; - while (true) { - if (node == this.menuitem) - break; - if (node == this) - return; - node = node.parentNode; - } - - this._parentMenupopup.hidePopup(); - ]]></handler> - </handlers> - </binding> - - <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem"> - <implementation> - <constructor><![CDATA[ - this.setAttribute("tooltiptext", this.getAttribute("acceltext")); - // TODO: Simplify this to this.setAttribute("acceltext", "") once bug - // 592424 is fixed - document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); - ]]></constructor> - </implementation> - </binding> - - <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic"> - <implementation> - <constructor><![CDATA[ - this.setAttribute("tooltiptext", this.getAttribute("acceltext")); - // TODO: Simplify this to this.setAttribute("acceltext", "") once bug - // 592424 is fixed - document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", ""); - ]]></constructor> - </implementation> - </binding> -</bindings> |