summaryrefslogtreecommitdiffstats
path: root/comm/suite/base/content/utilityOverlay.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/suite/base/content/utilityOverlay.js1969
1 files changed, 1969 insertions, 0 deletions
diff --git a/comm/suite/base/content/utilityOverlay.js b/comm/suite/base/content/utilityOverlay.js
new file mode 100644
index 0000000000..627203d435
--- /dev/null
+++ b/comm/suite/base/content/utilityOverlay.js
@@ -0,0 +1,1969 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Communicator Shared Utility Library
+ * for shared application glue for the Communicator suite of applications
+ **/
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ RecentWindow: "resource:///modules/RecentWindow.jsm",
+});
+
+// XPCOMUtils.defineLazyGetter(this, "Weave", function() {
+// let tmp = {};
+// ChromeUtils.import("resource://services-sync/main.js", tmp);
+// return tmp.Weave;
+// });
+
+/*
+ Note: All Editor/Composer-related methods have been moved to editorApplicationOverlay.js,
+ so app windows that require those must include editorTasksOverlay.xul
+*/
+
+/**
+ * Go into online/offline mode
+ **/
+
+const kProxyManual = ["network.proxy.ftp",
+ "network.proxy.http",
+ "network.proxy.socks",
+ "network.proxy.ssl"];
+var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
+var gShowBiDi = false;
+var gUtilityBundle = null;
+var gPrivate = null;
+
+function toggleOfflineStatus()
+{
+ var checkfunc;
+ try {
+ checkfunc = document.getElementById("offline-status").getAttribute('checkfunc');
+ }
+ catch (ex) {
+ checkfunc = null;
+ }
+
+ if (checkfunc) {
+ if (!eval(checkfunc)) {
+ // the pre-offline check function returned false, so don't go offline
+ return;
+ }
+ }
+ Services.io.manageOfflineStatus = false;
+ Services.io.offline = !Services.io.offline;
+}
+
+function setNetworkStatus(networkProxyType)
+{
+ try {
+ Services.prefs.setIntPref("network.proxy.type", networkProxyType);
+ }
+ catch (ex) {}
+}
+
+function InitProxyMenu()
+{
+ var networkProxyNo = document.getElementById("network-proxy-no");
+ var networkProxyManual = document.getElementById("network-proxy-manual");
+ var networkProxyPac = document.getElementById("network-proxy-pac");
+ var networkProxyWpad = document.getElementById("network-proxy-wpad");
+ var networkProxySystem = document.getElementById("network-proxy-system");
+
+ var proxyLocked = Services.prefs.prefIsLocked("network.proxy.type");
+ if (proxyLocked) {
+ networkProxyNo.setAttribute("disabled", "true");
+ networkProxyWpad.setAttribute("disabled", "true");
+ networkProxySystem.setAttribute("disabled", "true");
+ }
+ else {
+ networkProxyNo.removeAttribute("disabled");
+ networkProxyWpad.removeAttribute("disabled");
+ networkProxySystem.removeAttribute("disabled");
+ }
+
+ // If no proxy is configured, disable the menuitems.
+ // Checking for proxy manual settings.
+ var proxyManuallyConfigured = false;
+ for (var i = 0; i < kProxyManual.length; i++) {
+ if (Services.prefs.getStringPref(kProxyManual[i], "") != "") {
+ proxyManuallyConfigured = true;
+ break;
+ }
+ }
+
+ if (proxyManuallyConfigured && !proxyLocked) {
+ networkProxyManual.removeAttribute("disabled");
+ }
+ else {
+ networkProxyManual.setAttribute("disabled", "true");
+ }
+
+ //Checking for proxy PAC settings.
+ var proxyAutoConfigured = false;
+ if (Services.prefs.getStringPref("network.proxy.autoconfig_url", "") != "")
+ proxyAutoConfigured = true;
+
+ if (proxyAutoConfigured && !proxyLocked) {
+ networkProxyPac.removeAttribute("disabled");
+ }
+ else {
+ networkProxyPac.setAttribute("disabled", "true");
+ }
+
+ // The pref value 3 for network.proxy.type is unused to maintain
+ // backwards compatibility. Treat 3 equally to 0. See bug 115720.
+ var networkProxyStatus = [networkProxyNo, networkProxyManual, networkProxyPac,
+ networkProxyNo, networkProxyWpad,
+ networkProxySystem];
+ var networkProxyType = Services.prefs.getIntPref("network.proxy.type", 0);
+ networkProxyStatus[networkProxyType].setAttribute("checked", "true");
+}
+
+function setProxyTypeUI()
+{
+ var panel = document.getElementById("offline-status");
+ if (!panel)
+ return;
+
+ var onlineTooltip = "onlineTooltip" +
+ Services.prefs.getIntPref("network.proxy.type", 0);
+ panel.setAttribute("tooltiptext", gUtilityBundle.getString(onlineTooltip));
+}
+
+function SetStringPref(aPref, aValue)
+{
+ try {
+ Services.prefs.setStringPref(aPref, aValue);
+ } catch (e) {}
+}
+
+function GetLocalizedStringPref(aPrefName, aDefaultValue)
+{
+ try {
+ return Services.prefs.getComplexValue(aPrefName,
+ Ci.nsIPrefLocalizedString).data;
+ } catch (e) {
+ Cu.reportError("Couldn't get " + aPrefName + " pref: " + e);
+ }
+ return aDefaultValue;
+}
+
+function GetLocalFilePref(aName)
+{
+ try {
+ return Services.prefs.getComplexValue(aName,
+ Ci.nsIFile);
+ } catch (e) {}
+ return null;
+}
+
+/**
+ * Returns the Desktop folder.
+ */
+function GetDesktopFolder()
+{
+ return Services.dirsvc.get("Desk", Ci.nsIFile);
+}
+
+/**
+ * Returns the relevant nsIFile directory.
+ */
+function GetSpecialDirectory(aName)
+{
+ return Services.dirsvc.get(aName, Ci.nsIFile);
+}
+
+function GetUrlbarHistoryFile()
+{
+ var profileDir = GetSpecialDirectory("ProfD");
+ profileDir.append("urlbarhistory.sqlite");
+ return profileDir;
+}
+
+function setOfflineUI(offline)
+{
+ var broadcaster = document.getElementById("Communicator:WorkMode");
+ var panel = document.getElementById("offline-status");
+ if (!broadcaster || !panel) return;
+
+ // Checking for a preference "network.online", if it's locked, disabling
+ // network icon and menu item
+ if (Services.prefs.prefIsLocked("network.online"))
+ broadcaster.setAttribute("disabled", "true");
+
+ if (offline)
+ {
+ broadcaster.setAttribute("offline", "true");
+ broadcaster.setAttribute("checked", "true");
+ panel.removeAttribute("context");
+ panel.setAttribute("tooltiptext", gUtilityBundle.getString("offlineTooltip"));
+ }
+ else
+ {
+ broadcaster.removeAttribute("offline");
+ broadcaster.removeAttribute("checked");
+ panel.setAttribute("context", "networkProperties");
+ setProxyTypeUI();
+ }
+}
+
+function getBrowserURL() {
+
+ try {
+ var url = Services.prefs.getCharPref("browser.chromeURL");
+ if (url)
+ return url;
+ } catch(e) {
+ }
+ return "chrome://navigator/content/navigator.xul";
+}
+
+function goPreferences(paneID)
+{
+ //check for an existing pref window and focus it; it's not application modal
+ var lastPrefWindow = Services.wm.getMostRecentWindow("mozilla:preferences");
+ if (lastPrefWindow)
+ lastPrefWindow.focus();
+ else
+ openDialog("chrome://communicator/content/pref/preferences.xul",
+ "PrefWindow", "non-private,chrome,titlebar,dialog=no,resizable",
+ paneID);
+}
+
+function goToggleToolbar(id, elementID)
+{
+ var toolbar = document.getElementById(id);
+ if (!toolbar)
+ return;
+
+ var type = toolbar.getAttribute("type");
+ var toggleAttribute = type == "menubar" ? "autohide" : "hidden";
+ var hidden = toolbar.getAttribute(toggleAttribute) == "true";
+ var element = document.getElementById(elementID);
+
+ toolbar.setAttribute(toggleAttribute, !hidden);
+ if (element)
+ element.setAttribute("checked", hidden)
+
+ document.persist(id, toggleAttribute);
+ document.persist(elementID, "checked");
+
+ if (toolbar.hasAttribute("customindex"))
+ persistCustomToolbar(toolbar);
+
+}
+
+var gCustomizeSheet = false;
+
+function SuiteCustomizeToolbar(aMenuItem)
+{
+ let toolbar = aMenuItem.parentNode.triggerNode;
+ while (toolbar.localName != "toolbar") {
+ toolbar = toolbar.parentNode;
+ if (!toolbar)
+ return false;
+ }
+ return goCustomizeToolbar(toolbar.toolbox);
+}
+
+function goCustomizeToolbar(toolbox)
+{
+ /* If the toolbox has a method "customizeInit" then call it first.
+ The optional "customizeDone" method will be invoked by the callback
+ from the Customize Window so we don't need to take care of that */
+ if ("customizeInit" in toolbox)
+ toolbox.customizeInit();
+
+ var customizeURL = "chrome://communicator/content/customizeToolbar.xul";
+
+ gCustomizeSheet =
+ Services.prefs.getBoolPref("toolbar.customization.usesheet", false);
+
+ if (gCustomizeSheet) {
+ var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
+ var panel = document.getElementById("customizeToolbarSheetPopup");
+ sheetFrame.hidden = false;
+ sheetFrame.toolbox = toolbox;
+ sheetFrame.panel = panel;
+
+ // The document might not have been loaded yet, if this is the first time.
+ // If it is already loaded, reload it so that the onload initialization
+ // code re-runs.
+ if (sheetFrame.getAttribute("src") == customizeURL)
+ sheetFrame.contentWindow.location.reload();
+ else
+ sheetFrame.setAttribute("src", customizeURL);
+
+ // Open the panel, but make it invisible until the iframe has loaded so
+ // that the user doesn't see a white flash.
+ panel.style.visibility = "hidden";
+ toolbox.addEventListener("beforecustomization", function toolboxBeforeCustom() {
+ toolbox.removeEventListener("beforecustomization", toolboxBeforeCustom);
+ panel.style.removeProperty("visibility");
+ });
+ panel.openPopup(toolbox, "after_start", 0, 0);
+ return sheetFrame.contentWindow;
+ }
+ else {
+ return window.openDialog(customizeURL,
+ "",
+ "chrome,all,dependent",
+ toolbox);
+ }
+}
+
+function onViewToolbarsPopupShowing(aEvent, aInsertPoint)
+{
+ var popup = aEvent.target;
+ if (popup != aEvent.currentTarget)
+ return;
+
+ // Empty the menu
+ var deadItems = popup.getElementsByAttribute("toolbarid", "*");
+ for (let i = deadItems.length - 1; i >= 0; --i)
+ deadItems[i].remove();
+
+ // Thunderbird/Lightning function signature is:
+ // onViewToolbarsPopupShowing(aEvent, toolboxIds, aInsertPoint)
+ // where toolboxIds is either a string or an array of strings.
+ var firstMenuItem = aInsertPoint instanceof XULElement ? aInsertPoint
+ : popup.firstChild;
+
+ var toolbar = document.popupNode || popup;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.toolbox;
+ var externalToolbars = Array.from(toolbox.externalToolbars)
+ .filter(function(toolbar) {
+ return toolbar.hasAttribute("toolbarname")});
+ var toolbars = Array.from(toolbox.getElementsByAttribute("toolbarname", "*"))
+ .filter(function(toolbar) {
+ return !toolbar.hasAttribute("hideinmenu")});
+ toolbars = toolbars.concat(externalToolbars);
+ var menusep = document.getElementById("toolbarmode-sep");
+
+ var menubar = toolbox.getElementsByAttribute("type", "menubar").item(0);
+ if (!menubar || !toolbars.length) {
+ if (menusep)
+ menusep.hidden = true;
+ return;
+ }
+ if (menusep)
+ menusep.hidden = false;
+
+ toolbars.forEach(function(bar) {
+ let type = bar.getAttribute("type");
+ let toggleAttribute = type == "menubar" ? "autohide" : "hidden";
+ let isHidden = bar.getAttribute(toggleAttribute) == "true";
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("id", "toggle_" + bar.id);
+ menuItem.setAttribute("toolbarid", bar.id);
+ menuItem.setAttribute("type", "checkbox");
+ menuItem.setAttribute("label", bar.getAttribute("toolbarname"));
+ menuItem.setAttribute("accesskey", bar.getAttribute("accesskey"));
+ menuItem.setAttribute("checked", !isHidden);
+ popup.insertBefore(menuItem, firstMenuItem);
+ });
+}
+
+function onToolbarModePopupShowing(aEvent)
+{
+ var popup = aEvent.target;
+
+ var toolbar = document.popupNode;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.toolbox;
+
+ var mode = toolbar.getAttribute("mode") || "full";
+ var modePopup = document.getElementById("toolbarModePopup");
+ var radio = modePopup.getElementsByAttribute("value", mode);
+ radio[0].setAttribute("checked", "true");
+
+ var small = toolbar.getAttribute("iconsize") == "small";
+ var smallicons = document.getElementById("toolbarmode-smallicons");
+ smallicons.setAttribute("checked", small);
+ smallicons.setAttribute("disabled", mode == "text");
+
+ var end = toolbar.getAttribute("labelalign") == "end";
+ var labelalign = document.getElementById("toolbarmode-labelalign");
+ labelalign.setAttribute("checked", end);
+ labelalign.setAttribute("disabled", mode != "full");
+
+ var custommode = (toolbar.getAttribute("mode") || "full") !=
+ (toolbar.getAttribute("defaultmode") ||
+ toolbox.getAttribute("mode") ||
+ "full");
+ var customicon = (toolbar.getAttribute("iconsize") || "large") !=
+ (toolbar.getAttribute("defaulticonsize") ||
+ toolbox.getAttribute("iconsize") ||
+ "large");
+ var customalign = (toolbar.getAttribute("labelalign") || "bottom") !=
+ (toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign") ||
+ "bottom");
+ var custom = custommode || customicon || customalign ||
+ toolbar.hasAttribute("ignoremodepref");
+
+ var defmode = document.getElementById("toolbarmode-default");
+ defmode.setAttribute("checked", !custom);
+ defmode.setAttribute("disabled", !custom);
+
+ var command = document.getElementById("cmd_CustomizeToolbars");
+ var menuitem = document.getElementById("customize_toolbars");
+ menuitem.hidden = !command;
+ menuitem.previousSibling.hidden = !command;
+}
+
+function onViewToolbarCommand(aEvent)
+{
+ var toolbar = aEvent.originalTarget.getAttribute("toolbarid");
+ if (toolbar)
+ goToggleToolbar(toolbar);
+}
+
+function goSetToolbarState(aEvent)
+{
+ aEvent.stopPropagation();
+ var toolbar = document.popupNode;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.parentNode;
+
+ var target = aEvent.originalTarget;
+ var mode = target.value;
+ var radiogroup = target.getAttribute("name");
+ var primary = /toolbar-primary/.test(toolbar.getAttribute("class"));
+
+ switch (mode) {
+ case "smallicons":
+ var size = target.getAttribute("checked") == "true" ? "small" : "large";
+ toolbar.setAttribute("iconsize", size);
+ break;
+
+ case "end":
+ var align = target.getAttribute("checked") == "true" ? "end" : "bottom";
+ toolbar.setAttribute("labelalign", align);
+ break;
+
+ case "default":
+ toolbar.setAttribute("mode", toolbar.getAttribute("defaultmode") ||
+ toolbox.getAttribute("mode"));
+ toolbar.setAttribute("iconsize", toolbar.getAttribute("defaulticonsize") ||
+ toolbox.getAttribute("iconsize"));
+ toolbar.setAttribute("labelalign", toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign"));
+ if (primary)
+ toolbar.removeAttribute("ignoremodepref");
+ break;
+
+ default:
+ toolbar.setAttribute("mode", mode);
+ if (primary)
+ toolbar.setAttribute("ignoremodepref", "true");
+ break;
+ }
+ document.persist(toolbar.id, "mode");
+ document.persist(toolbar.id, "iconsize");
+ document.persist(toolbar.id, "labelalign");
+ if (primary)
+ document.persist(toolbar.id, "ignoremodepref");
+ if (toolbar.hasAttribute("customindex"))
+ persistCustomToolbar(toolbar);
+}
+
+function persistCustomToolbar(toolbar)
+{
+ var toolbox = toolbar.parentNode;
+ var name = toolbar.getAttribute("toolbarname").replace(" ", "_");
+ var attrs = ["mode", "iconsize", "labelalign", "hidden"];
+ for (let i = 0; i < attrs.length; i++) {
+ let value = toolbar.getAttribute(attrs[i]);
+ let attr = name + attrs[i];
+ toolbox.toolbarset.setAttribute(attr, value);
+ document.persist(toolbox.toolbarset.id, attr);
+ }
+}
+
+/* Common Customize Toolbar code */
+
+function toolboxCustomizeInit(menubarID)
+{
+ // Disable the toolbar context menu items
+ var menubar = document.getElementById(menubarID);
+ for (let i = 0; i < menubar.childNodes.length; ++i) {
+ let item = menubar.childNodes[i];
+ if (item.getAttribute("disabled") != "true") {
+ item.setAttribute("disabled", "true");
+ item.setAttribute("saved-disabled", "false");
+ }
+ }
+
+ var cmd = document.getElementById("cmd_CustomizeToolbars");
+ cmd.setAttribute("disabled", "true");
+}
+
+function toolboxCustomizeDone(menubarID, toolbox, aToolboxChanged)
+{
+ if (gCustomizeSheet) {
+ document.getElementById("customizeToolbarSheetIFrame").hidden = true;
+ document.getElementById("customizeToolbarSheetPopup").hidePopup();
+ if (content)
+ content.focus();
+ else
+ window.focus();
+ }
+
+ // Re-enable parts of the UI we disabled during the dialog
+ var menubar = document.getElementById(menubarID);
+ for (let i = 0; i < menubar.childNodes.length; ++i) {
+ let item = menubar.childNodes[i];
+ if (item.hasAttribute("saved-disabled")) {
+ item.removeAttribute("disabled");
+ item.removeAttribute("saved-disabled");
+ }
+ }
+
+ var cmd = document.getElementById("cmd_CustomizeToolbars");
+ cmd.removeAttribute("disabled");
+
+ var toolbars = toolbox.getElementsByAttribute("customindex", "*");
+ for (let i = 0; i < toolbars.length; ++i) {
+ persistCustomToolbar(toolbars[i]);
+ }
+}
+
+function toolboxCustomizeChange(toolbox, event)
+{
+ if (event != "reset")
+ return;
+ var toolbars = toolbox.getElementsByAttribute("toolbarname", "*");
+ for (let i = 0; i < toolbars.length; ++i) {
+ let toolbar = toolbars[i];
+ toolbar.setAttribute("labelalign",
+ toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign"));
+ document.persist(toolbar.id, "labelalign");
+ let primary = /toolbar-primary/.test(toolbar.getAttribute("class"));
+ if (primary) {
+ toolbar.removeAttribute("ignoremodepref");
+ document.persist(toolbar.id, "ignoremodepref");
+ }
+ }
+}
+
+function goClickThrobber(urlPref, aEvent)
+{
+ var url = GetLocalizedStringPref(urlPref);
+ if (url)
+ openUILinkIn(url, whereToOpenLink(aEvent, false, true, true));
+}
+
+function getTopWin(skipPopups) {
+ // If this is called in a browser window, use that window regardless of
+ // whether it's the frontmost window, since commands can be executed in
+ // background windows (bug 626148).
+ if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
+ (!skipPopups || top.toolbar.visible))
+ return top;
+
+ let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+ return RecentWindow.getMostRecentBrowserWindow({private: isPrivate,
+ allowPopups: !skipPopups});
+}
+
+function isRestricted( url )
+{
+ try {
+ let uri = Services.uriFixup
+ .createFixupURI(url, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
+ const URI_INHERITS_SECURITY_CONTEXT =
+ Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT;
+ return Services.netUtils
+ .URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT);
+ } catch (e) {
+ return false;
+ }
+}
+
+function goAbout(aProtocol)
+{
+ var target;
+ var url = "about:" + (aProtocol || "");
+ var defaultAboutState = Services.prefs.getIntPref("browser.link.open_external");
+
+ switch (defaultAboutState) {
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ target = "window";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW:
+ target = "current";
+ break;
+ default:
+ target = "tabfocused";
+ }
+ openUILinkIn(url, target);
+}
+
+function goTroubleshootingPage()
+{
+ goAbout("support");
+}
+
+function goReleaseNotes()
+{
+ // get release notes URL from prefs
+ try {
+ openUILink(Services.urlFormatter.formatURLPref("app.releaseNotesURL"));
+ }
+ catch (ex) { dump(ex); }
+}
+
+function openDictionaryList()
+{
+ try {
+ openAsExternal(Services.urlFormatter.formatURLPref("spellchecker.dictionaries.download.url"));
+ }
+ catch (ex) {
+ dump(ex);
+ }
+}
+
+// Prompt user to restart the browser in safe mode
+function safeModeRestart()
+{
+ // prompt the user to confirm
+ var promptTitle = gUtilityBundle.getString("safeModeRestartPromptTitle");
+ var promptMessage = gUtilityBundle.getString("safeModeRestartPromptMessage");
+ var restartText = gUtilityBundle.getString("safeModeRestartButton");
+ var checkboxText = gUtilityBundle.getString("safeModeRestartCheckbox");
+ var checkbox = { value: true };
+ var buttonFlags = (Services.prompt.BUTTON_POS_0 *
+ Services.prompt.BUTTON_TITLE_IS_STRING) +
+ (Services.prompt.BUTTON_POS_1 *
+ Services.prompt.BUTTON_TITLE_CANCEL) +
+ Services.prompt.BUTTON_POS_0_DEFAULT;
+
+ var rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
+ buttonFlags, restartText, null, null,
+ checkboxText, checkbox);
+ if (rv == 0) {
+ if (checkbox.value)
+ Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment)
+ .set("MOZ_SAFE_MODE_RESTART", "1");
+ BrowserUtils.restartApplication();
+ }
+}
+
+function checkForUpdates()
+{
+ var um = Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager);
+ var prompter = Cc["@mozilla.org/updates/update-prompt;1"]
+ .createInstance(Ci.nsIUpdatePrompt);
+
+ // If there's an update ready to be applied, show the "Update Downloaded"
+ // UI instead and let the user know they have to restart the browser for
+ // the changes to be applied.
+ if (um.activeUpdate && um.activeUpdate.state == "pending")
+ prompter.showUpdateDownloaded(um.activeUpdate);
+ else
+ prompter.checkForUpdates();
+}
+
+function updateCheckUpdatesItem()
+{
+ var hasUpdater = "nsIApplicationUpdateService" in Ci;
+ var checkForUpdates = document.getElementById("checkForUpdates");
+
+ if (!hasUpdater)
+ {
+ var updateSeparator = document.getElementById("updateSeparator");
+
+ checkForUpdates.hidden = true;
+ updateSeparator.hidden = true;
+ return;
+ }
+
+ var updates = Cc["@mozilla.org/updates/update-service;1"]
+ .getService(Ci.nsIApplicationUpdateService);
+ var um = Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager);
+
+ // Disable the UI if the update enabled pref has been locked by the
+ // administrator or if we cannot update for some other reason.
+ var canCheckForUpdates = updates.canCheckForUpdates;
+ checkForUpdates.setAttribute("disabled", !canCheckForUpdates);
+
+ if (!canCheckForUpdates)
+ return;
+
+ // By default, show "Check for Updates..."
+ var key = "default";
+ if (um.activeUpdate) {
+ switch (um.activeUpdate.state) {
+ case "downloading":
+ // If we're downloading an update at present, show the text:
+ // "Downloading SeaMonkey x.x..." otherwise we're paused, and show
+ // "Resume Downloading SeaMonkey x.x..."
+ key = updates.isDownloading ? "downloading" : "resume";
+ break;
+ case "pending":
+ // If we're waiting for the user to restart, show: "Apply Downloaded
+ // Updates Now..."
+ key = "pending";
+ break;
+ }
+ }
+
+ // If there's an active update, substitute its name into the label
+ // we show for this item, otherwise display a generic label.
+ if (um.activeUpdate && um.activeUpdate.name)
+ checkForUpdates.label = gUtilityBundle.getFormattedString("updatesItem_" + key,
+ [um.activeUpdate.name]);
+ else
+ checkForUpdates.label = gUtilityBundle.getString("updatesItem_" + key + "Fallback");
+
+ checkForUpdates.accessKey = gUtilityBundle.getString("updatesItem_" + key + "AccessKey");
+
+ if (um.activeUpdate && updates.isDownloading)
+ checkForUpdates.setAttribute("loading", "true");
+ else
+ checkForUpdates.removeAttribute("loading");
+}
+
+// update menu items that rely on focus
+function goUpdateGlobalEditMenuItems()
+{
+ goUpdateCommand('cmd_undo');
+ goUpdateCommand('cmd_redo');
+ goUpdateCommand('cmd_cut');
+ goUpdateCommand('cmd_copy');
+ goUpdateCommand('cmd_paste');
+ goUpdateCommand('cmd_selectAll');
+ goUpdateCommand('cmd_delete');
+ if (gShowBiDi)
+ goUpdateCommand('cmd_switchTextDirection');
+}
+
+// update menu items that rely on the current selection
+function goUpdateSelectEditMenuItems()
+{
+ goUpdateCommand('cmd_cut');
+ goUpdateCommand('cmd_copy');
+ goUpdateCommand('cmd_delete');
+ goUpdateCommand('cmd_selectAll');
+}
+
+// update menu items that relate to undo/redo
+function goUpdateUndoEditMenuItems()
+{
+ goUpdateCommand('cmd_undo');
+ goUpdateCommand('cmd_redo');
+}
+
+// update menu items that depend on clipboard contents
+function goUpdatePasteMenuItems()
+{
+ goUpdateCommand('cmd_paste');
+}
+
+// update Find As You Type menu items, they rely on focus
+function goUpdateFindTypeMenuItems()
+{
+ goUpdateCommand('cmd_findTypeText');
+ goUpdateCommand('cmd_findTypeLinks');
+}
+
+// Gather all descendent text under given document node.
+function gatherTextUnder(root)
+{
+ var text = "";
+ var node = root.firstChild;
+ var depth = 1;
+ while ( node && depth > 0 ) {
+ // See if this node is text.
+ if ( node.nodeType == Node.TEXT_NODE ) {
+ // Add this text to our collection.
+ text += " " + node.data;
+ } else if ( node instanceof HTMLImageElement ) {
+ // If it has an alt= attribute, add that.
+ var altText = node.getAttribute( "alt" );
+ if ( altText && altText != "" ) {
+ text += " " + altText;
+ }
+ }
+ // Find next node to test.
+ // First, see if this node has children.
+ if ( node.hasChildNodes() ) {
+ // Go to first child.
+ node = node.firstChild;
+ depth++;
+ } else {
+ // No children, try next sibling.
+ if ( node.nextSibling ) {
+ node = node.nextSibling;
+ } else {
+ // Last resort is a sibling of an ancestor.
+ while ( node && depth > 0 ) {
+ node = node.parentNode;
+ depth--;
+ if ( node.nextSibling ) {
+ node = node.nextSibling;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Strip leading and trailing whitespaces,
+ // then compress remaining whitespaces.
+ return text.trim().replace(/\s+/g, " ");
+}
+
+var offlineObserver = {
+ observe: function(subject, topic, state) {
+ // sanity checks
+ if (topic != "network:offline-status-changed") return;
+ setOfflineUI(state == "offline");
+ }
+}
+
+var proxyTypeObserver = {
+ observe: function(subject, topic, state) {
+ // sanity checks
+ if (state == "network.proxy.type" && !Services.io.offline)
+ setProxyTypeUI();
+ }
+}
+
+function utilityOnLoad(aEvent)
+{
+ gUtilityBundle = document.getElementById("bundle_utilityOverlay");
+
+ var broadcaster = document.getElementById("Communicator:WorkMode");
+ if (!broadcaster) return;
+
+ Services.obs.addObserver(offlineObserver, "network:offline-status-changed");
+ // make sure we remove this observer later
+ Services.prefs.addObserver("network.proxy.type", proxyTypeObserver);
+
+ addEventListener("unload", utilityOnUnload, false);
+
+ // set the initial state
+ setOfflineUI(Services.io.offline);
+
+ // Check for system proxy settings class and show menuitem if present
+ if ("@mozilla.org/system-proxy-settings;1" in Cc &&
+ document.getElementById("network-proxy-system"))
+ document.getElementById("network-proxy-system").hidden = false;
+}
+
+function utilityOnUnload(aEvent)
+{
+ Services.obs.removeObserver(offlineObserver, "network:offline-status-changed");
+ Services.prefs.removeObserver("network.proxy.type", proxyTypeObserver);
+}
+
+addEventListener("load", utilityOnLoad, false);
+
+/**
+ * example use:
+ * suggestUniqueFileName("testname", ".txt", ["testname.txt", "testname(2).txt"])
+ * returns "testname(3).txt"
+ * does not check file system for existing files
+ *
+ * @param aBaseName base name for generating unique filenames.
+ *
+ * @param aExtension extension name to use for the generated filename.
+ *
+ * @param aExistingNames array of names in use.
+ *
+ * @return suggested filename as a string.
+ */
+function suggestUniqueFileName(aBaseName, aExtension, aExistingNames)
+{
+ var suffix = 1;
+ aBaseName = validateFileName(aBaseName);
+ var suggestion = aBaseName + aExtension;
+ while (aExistingNames.includes(suggestion))
+ {
+ suffix++;
+ suggestion = aBaseName + "(" + suffix + ")" + aExtension;
+ }
+ return suggestion;
+}
+
+function focusElement(aElement)
+{
+ if (isElementVisible(aElement))
+ aElement.focus();
+}
+
+function isElementVisible(aElement)
+{
+ if (!aElement)
+ return false;
+
+ // If aElement or a direct or indirect parent is hidden or collapsed,
+ // height, width or both will be 0.
+ var bo = aElement.boxObject;
+ return (bo.height > 0 && bo.width > 0);
+}
+
+function makeURLAbsolute(aBase, aUrl, aCharset)
+{
+ // Construct nsIURL.
+ return Services.io.newURI(aUrl, aCharset,
+ Services.io.newURI(aBase, aCharset)).spec;
+}
+
+/**
+ * whereToLoadExternalLink: Returns values for opening a new external link.
+ *
+ * @returns (object[]} an array of objects with the following structure:
+ * - (string) where location where to open the link.
+ * - (bool) loadInBackground load url in background.
+ * - (bool) Focus browser after load.
+ */
+function whereToLoadExternalLink() {
+ let openParms = {
+ where: null,
+ loadInBackground: false,
+ avoidBrowserFocus: false,
+ }
+
+ switch (Services.prefs.getIntPref("browser.link.open_external")) {
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ openParms.where = "window";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB:
+ openParms.where = "tab";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW:
+ openParms.where = "current";
+ break;
+ default:
+ console.log("Check pref browser.link.open_external");
+ openParms.where = "current";
+ }
+ openParms.loadInBackground =
+ Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
+
+ openParms.avoidBrowserFocus =
+ Services.prefs.getBoolPref("browser.tabs.avoidBrowserFocus");
+
+ return openParms;
+}
+
+function openAsExternal(aURL) {
+ let openParms = whereToLoadExternalLink();
+
+ openNewTabWindowOrExistingWith(aURL, openParms.where, null,
+ openParms.loadInBackground);
+}
+
+/**
+ * openNewTabWith: opens a new tab with the given URL.
+ * openNewWindowWith: opens a new window with the given URL.
+ * openNewPrivateWith: opens a private window with the given URL.
+ *
+ * @param aURL
+ * The URL to open (as a string).
+ * @param aDocument
+ * The document from which the URL came, or null. This is used to set
+ * the referrer header and to do a security check of whether the
+ * document is allowed to reference the URL. If null, there will be no
+ * referrer header and no security check.
+ * @param aPostData
+ * Form POST data, or null.
+ * @param aEvent
+ * The triggering event (for the purpose of determining whether to open
+ * in the background), or null.
+ * @param aAllowThirdPartyFixup
+ * If true, then we allow the URL text to be sent to third party
+ * services (e.g., Google's I Feel Lucky) for interpretation. This
+ * parameter may be undefined in which case it is treated as false.
+ * @param [optional] aReferrer
+ * If aDocument is null, then this will be used as the referrer.
+ * There will be no security check.
+ * @param [optional] aReferrerPolicy
+ * Referrer policy - Ci.nsIHttpChannel.REFERRER_POLICY_*.
+ */
+function openNewPrivateWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy) {
+ return openNewTabWindowOrExistingWith(aURL, "private", aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy) {
+ return openNewTabWindowOrExistingWith(aURL, "window", aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewTabWith(aURL, aDocument, aPostData, aEvent,
+ aAllowThirdPartyFixup, aReferrer, aReferrerPolicy) {
+ let where = aEvent && aEvent.shiftKey ? "tabshifted" : "tab";
+ return openNewTabWindowOrExistingWith(aURL, where, aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewTabWindowOrExistingWith(aURL, aWhere, aDocument,
+ aLoadInBackground, aPostData,
+ aAllowThirdPartyFixup, aReferrer,
+ aReferrerPolicy) {
+ // Make sure we are allowed to open this url
+ if (aDocument)
+ urlSecurityCheck(aURL, aDocument.nodePrincipal);
+
+ // Where appropriate we want to pass the charset of the
+ // current document over to a new tab / window.
+ var originCharset = null;
+ if (aWhere != "current") {
+ originCharset = aDocument && aDocument.characterSet;
+ if (!originCharset &&
+ document.documentElement.getAttribute("windowtype") == "navigator:browser")
+ originCharset = window.content.document.characterSet;
+ }
+
+ var isPrivate = false;
+ if (aWhere == "private") {
+ aWhere = "window";
+ isPrivate = true;
+ }
+ var referrerURI = aDocument ? aDocument.documentURIObject : aReferrer;
+ return openLinkIn(aURL, aWhere,
+ { charset: originCharset,
+ postData: aPostData,
+ inBackground: aLoadInBackground,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ referrerURI: referrerURI,
+ referrerPolicy: aReferrerPolicy,
+ private: isPrivate, });
+}
+
+/**
+ * Handle command events bubbling up from error page content
+ * called from oncommand by <browser>s that support error pages
+ */
+function BrowserOnCommand(event)
+{
+ // Don't trust synthetic events
+ if (!event.isTrusted)
+ return;
+
+ const ot = event.originalTarget;
+ const ownerDoc = ot.ownerDocument;
+ const docURI = ownerDoc.documentURI;
+ const buttonID = ot.getAttribute("anonid");
+
+ // If the event came from an ssl error page, it is probably either the "Add
+ // Exception" or "Get Me Out Of Here" button
+ if (docURI.startsWith("about:certerror?")) {
+ if (buttonID == "exceptionDialogButton") {
+ let docshell = ownerDoc.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let securityInfo = docshell.failedChannel.securityInfo;
+ let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus;
+
+ let params = { exceptionAdded : false, sslStatus : sslStatus };
+
+ switch (Services.prefs.getIntPref("browser.ssl_override_behavior", 2)) {
+ case 2 : // Pre-fetch & pre-populate.
+ params.prefetchCert = true;
+ // Fall through.
+ case 1 : // Pre-populate.
+ params.location = ownerDoc.location.href;
+ }
+
+ window.openDialog('chrome://pippki/content/exceptionDialog.xul',
+ '', 'chrome,centerscreen,modal', params);
+
+ // If the user added the exception cert, attempt to reload the page
+ if (params.exceptionAdded)
+ ownerDoc.location.reload();
+ }
+ else if (buttonID == "getMeOutOfHereButton") {
+ // Redirect them to a known-functioning page, default start page
+ getMeOutOfHere();
+ }
+ }
+ else if (docURI.startsWith("about:blocked")) {
+ // The event came from a button on a malware/phishing block page
+ // First check whether the reason, so that we can
+ // use the right strings/links
+ let reason = "phishing";
+
+ if (/e=malwareBlocked/.test(docURI)) {
+ reason = "malware";
+ } else if (/e=unwantedBlocked/.test(docURI)) {
+ reason = "unwanted";
+ } else if (/e=harmfulBlocked/.test(docURI)) {
+ reason = "harmful";
+ }
+
+ let docShell = ownerDoc.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let blockedInfo = {};
+ if (docShell.failedChannel) {
+ let classifiedChannel = docShell.failedChannel.
+ QueryInterface(Ci.nsIClassifiedChannel);
+ if (classifiedChannel) {
+ let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
+
+ let reportUri = httpChannel.URI.clone();
+
+ // Remove the query to avoid leaking sensitive data
+ if (reportUri instanceof Ci.nsIURL) {
+ reportUri = reportUri.mutate().setQuery("").finalize();
+ }
+
+ blockedInfo = { list: classifiedChannel.matchedList,
+ provider: classifiedChannel.matchedProvider,
+ uri: reportUri.asciiSpec };
+ }
+ }
+
+ switch (buttonID) {
+ case "getMeOutOfHereButton":
+ getMeOutOfHere();
+ break;
+ case "reportButton":
+ // This is the "Why is this site blocked" button. We redirect
+ // to the generic page describing phishing/malware protection.
+ try {
+ loadURI(Services.urlFormatter.formatURLPref("browser.safebrowsing.warning.infoURL"));
+ } catch (e) {
+ Cu.reportError("Couldn't get phishing info URL: " + e);
+ }
+ break;
+ case "ignoreWarningButton":
+ if (Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
+ getBrowser().getNotificationBox().ignoreSafeBrowsingWarning(reason, blockedInfo);
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * Re-direct the browser to a known-safe page. This function is
+ * used when, for example, the user browses to a known malware page
+ * and is presented with about:blocked. The "Get me out of here!"
+ * button should take the user to the default start page so that even
+ * when their own homepage is infected, we can get them somewhere safe.
+ */
+function getMeOutOfHere() {
+ // Get the start page from the *default* pref branch, not the user's
+ var prefs = Services.prefs.getDefaultBranch(null);
+ var url = "about:blank";
+ try {
+ url = prefs.getComplexValue("browser.startup.homepage",
+ Ci.nsIPrefLocalizedString).data;
+ } catch(e) {}
+ loadURI(url);
+}
+
+function popupNotificationMenuShowing(event)
+{
+ var notificationbox = document.popupNode.parentNode.control;
+ var uri = notificationbox.activeBrowser.currentURI;
+ var allowPopupsForSite = document.getElementById("allowPopupsForSite");
+ allowPopupsForSite.notificationbox = notificationbox;
+ var showPopupManager = document.getElementById("showPopupManager");
+
+ // Only offer this menu item for the top window.
+ // See bug 280536 for problems with frames and iframes.
+ try {
+ // uri.host generates an exception on nsISimpleURIs.
+ var allowString = gUtilityBundle.getFormattedString("popupAllow", [uri.host || uri.spec]);
+ allowPopupsForSite.setAttribute("label", allowString);
+ showPopupManager.hostport = uri.hostPort;
+ allowPopupsForSite.hidden = gPrivate;
+ } catch (ex) {
+ allowPopupsForSite.hidden = true;
+ showPopupManager.hostport = "";
+ }
+
+ var separator = document.getElementById("popupNotificationMenuSeparator");
+ separator.hidden = !createShowPopupsMenu(event.target, notificationbox.activeBrowser);
+}
+
+function RemovePopupsItems(parent)
+{
+ while (parent.lastChild && parent.lastChild.hasAttribute("popupReportIndex"))
+ parent.lastChild.remove();
+}
+
+function createShowPopupsMenu(parent, browser)
+{
+ if (!browser)
+ return false;
+
+ if (!browser.blockedPopups ||
+ browser.blockedPopups.count == 0)
+ return false;
+
+ parent.browser = browser;
+
+ browser.retrieveListOfBlockedPopups().then(blockedPopups => {
+
+ for (var i = 0; i < blockedPopups.length; i++) {
+
+ let blockedPopup = blockedPopups[i];
+ // popupWindowURI will be null if the file picker popup is blocked.
+ if (!blockedPopup.popupWindowURIspec)
+ continue;
+
+ let str = gUtilityBundle.getFormattedString("popupMenuShow", [blockedPopup.popupWindowURIspec]);
+ // Check for duplicates in the blockedPopups list and reuse the old menuitem.
+ let menuitem = parent.getElementsByAttribute("label", str).item(0);
+ if (!menuitem) {
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", str);
+ }
+ menuitem.setAttribute("popupReportIndex", i);
+ parent.appendChild(menuitem);
+ }
+ }, null);
+
+ return parent.getElementsByAttribute("popupReportIndex", "*").item(0) != null;
+}
+
+function popupBlockerMenuCommand(target)
+{
+ if (target.hasAttribute("popupReportIndex"))
+ target.parentNode.browser.unblockPopup(target.getAttribute("popupReportIndex"));
+}
+
+function hostUrl()
+{
+ var url = "";
+ try {
+ url = getBrowser().currentURI.scheme + "://" + getBrowser().currentURI.hostPort;
+ } catch (e) {}
+ return url;
+}
+
+function disablePopupBlockerNotifications()
+{
+ Services.prefs.setBoolPref("privacy.popups.showBrowserMessage", false);
+}
+
+// Used as an onclick handler for UI elements with link-like behavior.
+// e.g. onclick="checkForMiddleClick(this, event);"
+function checkForMiddleClick(node, event) {
+ // We should be using the disabled property here instead of the attribute,
+ // but some elements that this function is used with don't support it (e.g.
+ // menuitem).
+ if (node.getAttribute("disabled") == "true")
+ return; // Do nothing
+
+ if (event.button == 1) {
+ /* Execute the node's oncommand or command.
+ *
+ * XXX: we should use node.oncommand(event) once bug 246720 is fixed.
+ */
+ var target = node.hasAttribute("oncommand") ? node :
+ node.ownerDocument.getElementById(node.getAttribute("command"));
+ var fn = new Function("event", target.getAttribute("oncommand"));
+ fn.call(target, event);
+
+ // If the middle-click was on part of a menu, close the menu.
+ // (Menus close automatically with left-click but not with middle-click.)
+ closeMenus(event.target);
+ }
+}
+
+// Closes all popups that are ancestors of the node.
+function closeMenus(node) {
+ if ("tagName" in node) {
+ if (node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ && (node.tagName == "menupopup" || node.tagName == "popup"))
+ node.hidePopup();
+
+ closeMenus(node.parentNode);
+ }
+}
+
+/**
+ * Toggle a splitter to show or hide some piece of UI (e.g. the message preview
+ * pane).
+ *
+ * @param aSplitterId the splitter that should be toggled
+ */
+function togglePaneSplitter(aSplitterId)
+{
+ var splitter = document.getElementById(aSplitterId);
+ if (splitter.getAttribute("state") == "collapsed")
+ splitter.setAttribute("state", "open");
+ else
+ splitter.setAttribute("state", "collapsed");
+}
+
+/* openUILink handles clicks on UI elements that cause URLs to load.
+ *
+ * As the third argument, you may pass an object with the same properties as
+ * accepted by openUILinkIn, plus "ignoreButton" and "ignoreSave".
+ *
+ * Note: Firefox uses aIgnoreAlt while SeaMonkey uses aIgnoreSave because in
+ * SeaMonkey, Save can be Alt or Shift depending on ui.key.saveLink.shift.
+ *
+ * For API compatibility with Firefox the object version uses params.ignoreAlt
+ * although for SeaMonkey it is effectively ignoreSave.
+ */
+function openUILink(url, aEvent, aIgnoreButton, aIgnoreSave,
+ aAllowThirdPartyFixup, aPostData, aReferrerURI) {
+ var params;
+ if (aIgnoreButton && typeof aIgnoreButton == "object") {
+ params = aIgnoreButton;
+
+ // don't forward "ignoreButton" and "ignoreSave" to openUILinkIn.
+ aIgnoreButton = params.ignoreButton;
+ aIgnoreSave = params.ignoreAlt;
+ delete params.ignoreButton;
+ delete params.ignoreAlt;
+ }
+ else {
+ params = {allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostData,
+ referrerURI: aReferrerURI,
+ referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+ initiatingDoc: aEvent ? aEvent.target.ownerDocument : document,}
+ }
+
+ var where = whereToOpenLink(aEvent, aIgnoreButton, aIgnoreSave);
+ return openUILinkIn(url, where, params);
+}
+
+/* whereToOpenLink() looks at an event to decide where to open a link.
+ *
+ * The event may be a mouse event (click, double-click, middle-click) or keypress event (enter).
+ *
+ * The logic for modifiers is as following:
+ * If browser.tabs.opentabfor.middleclick is true, then Ctrl (or Meta) and middle-click
+ * open a new tab, depending on Shift, browser.tabs.loadInBackground, and
+ * ignoreBackground.
+ * Otherwise if middlemouse.openNewWindow is true, then Ctrl (or Meta) and middle-click
+ * open a new window.
+ * Otherwise if middle-click is pressed then nothing happens.
+ * Save is Alt or Shift depending on the ui.key.saveLink.shift preference.
+ * Otherwise if Alt, or Shift, or Ctrl (or Meta) is pressed then nothing happens.
+ * Otherwise the most recent browser is used for left clicks.
+ *
+ * Exceptions:
+ * - Alt is ignored for menu items selected using the keyboard so you don't accidentally save stuff.
+ * - Alt is hard to use in context menus, because pressing Alt closes the menu.
+ * - Alt can't be used on the bookmarks toolbar because Alt is used for "treat this as something draggable".
+ * - The button is ignored for the middle-click-paste-URL feature, since it's always a middle-click.
+ */
+function whereToOpenLink(e, ignoreButton, ignoreSave, ignoreBackground = false)
+{
+ // This method must treat a null event like a left click without modifier keys (i.e.
+ // e = { shiftKey:false, ctrlKey:false, metaKey:false, altKey:false, button:0 })
+ // for compatibility purposes.
+ if (!e)
+ return "current";
+
+ var shift = e.shiftKey;
+ var ctrl = e.ctrlKey;
+ var meta = e.metaKey;
+ var alt = e.altKey && !ignoreSave;
+
+ // ignoreButton allows "middle-click paste" to use function without always opening in a new window.
+ var middle = !ignoreButton && e.button == 1;
+
+ // Don't do anything special with right-mouse clicks. They're probably clicks on context menu items.
+
+ // On macOS ctrl is not evaluated.
+ var metaKey = AppConstants.platform == "macosx" ? meta : ctrl;
+
+ if (metaKey || middle) {
+ if (Services.prefs.getBoolPref("browser.tabs.opentabfor.middleclick", true))
+ return ignoreBackground ? "tabfocused" : shift ? "tabshifted" : "tab";
+ if (Services.prefs.getBoolPref("middlemouse.openNewWindow", true))
+ return "window";
+ if (middle)
+ return null;
+ }
+ if (!ignoreSave) {
+ if (Services.prefs.getBoolPref("ui.key.saveLink.shift", true) ? shift : alt)
+ return "save";
+ }
+ if (alt || shift || meta || ctrl)
+ return null;
+
+ return "current";
+}
+
+/* openUILinkIn opens a URL in a place specified by the parameter |where|.
+ *
+ * |where| can be:
+ * "current" current tab (if there aren't any browser windows, then in a new window instead)
+ * "tab" new tab (if there aren't any browser windows, then in a new window instead)
+ * "tabshifted" same as "tab" but in background if default is to select new tabs, and vice versa
+ * "tabfocused" same as "tab" but explicitly focus new tab
+ * "private" private browsing window
+ * "window" new window
+ * "save" save to disk (with no filename hint!)
+ *
+ * aAllowThirdPartyFixup controls whether third party services such as Google's
+ * I'm Feeling Lucky are allowed to interpret this URL. This parameter may be
+ * undefined, which is treated as false.
+ *
+ * Instead of aAllowThirdPartyFixup, you may also pass an object with any of
+ * these properties:
+ * allowThirdPartyFixup (boolean)
+ * postData (nsIInputStream)
+ * referrerURI (nsIURI)
+ * relatedToCurrent (boolean)
+ * initiatingDoc (document)
+ * userContextId (unsigned int)
+ */
+function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
+ var params;
+
+ if (arguments.length == 3 && typeof arguments[2] == "object") {
+ params = aAllowThirdPartyFixup;
+ } else {
+ params = {
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostData,
+ referrerURI: aReferrerURI,
+ referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+ };
+ }
+
+ if (where == "private") {
+ where = "window";
+ params.private = true;
+ }
+
+ params.fromChrome = true;
+
+ return openLinkIn(url, where, params);
+}
+
+function openLinkIn(url, where, params)
+{
+ if (!where || !url)
+ return null;
+
+ var aFromChrome = params.fromChrome;
+ var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
+ var aPostData = params.postData;
+ var aCharset = params.charset;
+ var aReferrerURI = params.referrerURI;
+ var aReferrerPolicy = ("referrerPolicy" in params ?
+ params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
+ var aRelatedToCurrent = params.relatedToCurrent;
+ var aAllowMixedContent = params.allowMixedContent;
+ var aInBackground = params.inBackground;
+ var aAvoidBrowserFocus = params.avoidBrowserFocus;
+ var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+ var aInitiatingDoc = params.initiatingDoc ? params.initiatingDoc : document;
+ var aIsPrivate = params.private;
+ var aNoReferrer = params.noReferrer;
+ var aUserContextId = params.userContextId;
+ var aPrincipal = params.originPrincipal;
+ var aTriggeringPrincipal = params.triggeringPrincipal;
+ var aForceAboutBlankViewerInCurrent =
+ params.forceAboutBlankViewerInCurrent;
+
+ if (where == "save") {
+ saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+ aInitiatingDoc);
+ return null;
+ }
+
+ // Establish which window we'll load the link in.
+ var w = getTopWin();
+ // We don't want to open tabs in popups, so try to find a non-popup window in
+ // that case.
+ if ((where == "tab" || where == "tabshifted") && w && !w.toolbar.visible) {
+ w = getTopWin(true);
+ aRelatedToCurrent = false;
+ }
+
+ // Teach the principal about the right OA to use, e.g. in case when
+ // opening a link in a new private window, or in a new container tab.
+ // Please note we do not have to do that for SystemPrincipals and we
+ // can not do it for NullPrincipals since NullPrincipals are only
+ // identical if they actually are the same object (See Bug: 1346759)
+ function useOAForPrincipal(principal) {
+ if (principal && principal.isCodebasePrincipal) {
+ let attrs = {
+ userContextId: aUserContextId,
+ };
+ return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
+ }
+ return principal;
+ }
+ aPrincipal = useOAForPrincipal(aPrincipal);
+ aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
+
+ if (!w || where == "window") {
+ let features = "chrome,dialog=no,all";
+ if (aIsPrivate) {
+ features += ",private";
+ // To prevent regular browsing data from leaking to private browsing
+ // sites, strip the referrer when opening a new private window.
+ aNoReferrer = true;
+ }
+
+ // This propagates to window.arguments.
+ var sa = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+
+ var wuri = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ wuri.data = url;
+
+ let charset = null;
+ if (aCharset) {
+ charset = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ charset.data = "charset=" + aCharset;
+ }
+
+ var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
+
+ var referrerURISupports = null;
+ if (aReferrerURI && !aNoReferrer) {
+ referrerURISupports = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ referrerURISupports.data = aReferrerURI.spec;
+ }
+
+ var referrerPolicySupports = Cc["@mozilla.org/supports-PRUint32;1"].
+ createInstance(Ci.nsISupportsPRUint32);
+ referrerPolicySupports.data = aReferrerPolicy;
+
+ var userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].
+ createInstance(Ci.nsISupportsPRUint32);
+ userContextIdSupports.data = aUserContextId;
+
+ sa.appendElement(wuri);
+ sa.appendElement(charset);
+ sa.appendElement(referrerURISupports);
+ sa.appendElement(aPostData);
+ sa.appendElement(allowThirdPartyFixupSupports);
+ sa.appendElement(referrerPolicySupports);
+ sa.appendElement(userContextIdSupports);
+ sa.appendElement(aPrincipal);
+ sa.appendElement(aTriggeringPrincipal);
+
+ const sourceWindow = (w || window);
+ Services.ww.openWindow(sourceWindow, getBrowserURL(), null, features, sa);
+ return;
+ }
+
+ let loadInBackground = aInBackground;
+ if (loadInBackground == null) {
+ loadInBackground =
+ aFromChrome ? false :
+ Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+ }
+
+ if (aAvoidBrowserFocus == null) {
+ aAvoidBrowserFocus =
+ Services.prefs.getBoolPref("browser.tabs.avoidBrowserFocus", false);
+ }
+
+ // reuse the browser if its current tab is empty
+ if (isBrowserEmpty(w.getBrowser()))
+ where = "current";
+
+ switch (where) {
+ case "current":
+ let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+
+ if (aAllowThirdPartyFixup) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
+ }
+ if (aDisallowInheritPrincipal) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+ }
+
+ if (aForceAboutBlankViewerInCurrent) {
+ w.gBrowser.selectedBrowser.createAboutBlankContentViewer(aPrincipal);
+ }
+
+ w.getBrowser().loadURIWithFlags(url, {
+ triggeringPrincipal: aTriggeringPrincipal,
+ flags,
+ referrerURI: aNoReferrer ? null : aReferrerURI,
+ referrerPolicy: aReferrerPolicy,
+ postData: aPostData,
+ userContextId: aUserContextId
+ });
+ if (!aAvoidBrowserFocus) {
+ w.content.focus();
+ }
+ break;
+
+ case "tabfocused":
+ // forces tab to be focused
+ loadInBackground = true;
+ // fall through
+ case "tabshifted":
+ loadInBackground = !loadInBackground;
+ // fall through
+ case "tab":
+ var browser = w.getBrowser();
+ var tab = browser.addTab(url, {
+ referrerURI: aReferrerURI,
+ referrerPolicy: aReferrerPolicy,
+ charset: aCharset,
+ postData: aPostData,
+ ownerTab: loadInBackground ? null : browser.selectedTab,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ relatedToCurrent: aRelatedToCurrent,
+ allowMixedContent: aAllowMixedContent,
+ noReferrer: aNoReferrer,
+ userContextId: aUserContextId,
+ originPrincipal: aPrincipal,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ if (!loadInBackground) {
+ browser.selectedTab = tab;
+ }
+ if (!aAvoidBrowserFocus) {
+ w.content.focus();
+ }
+
+ break;
+ }
+
+ return w;
+}
+
+// This opens the URLs contained in the given array in new tabs
+// of the most recent window, creates a new window if necessary.
+function openUILinkArrayIn(urlArray, where, allowThirdPartyFixup)
+{
+ if (!where || !urlArray.length)
+ return null;
+
+ if (where == "save") {
+ for (var i = 0; i < urlArray.length; i++)
+ saveURL(urlArray[i], null, null, true, true, null, document);
+ return null;
+ }
+
+ var w = getTopWin();
+
+ if (!w || where == "window") {
+ return window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no",
+ urlArray.join("\n"), // Pretend that we're a home page group
+ null, null, null, allowThirdPartyFixup);
+ }
+
+ var loadInBackground =
+ Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+
+ var browser = w.getBrowser();
+ switch (where) {
+ case "current":
+ w.loadURI(urlArray[0], null, null, allowThirdPartyFixup);
+ w.content.focus();
+ break;
+ case "tabshifted":
+ loadInBackground = !loadInBackground;
+ // fall through
+ case "tab":
+ var tab = browser.addTab(urlArray[0], {allowThirdPartyFixup: allowThirdPartyFixup});
+ if (!loadInBackground) {
+ browser.selectedTab = tab;
+ w.content.focus();
+ }
+ }
+ var relatedToCurrent = where == "current";
+ for (var i = 1; i < urlArray.length; i++)
+ browser.addTab(urlArray[i], {allowThirdPartyFixup: allowThirdPartyFixup, relatedToCurrent: relatedToCurrent});
+
+ return w;
+}
+
+/**
+ * Switch to a tab that has a given URI, and focusses its browser window.
+ * If a matching tab is in this window, it will be switched to. Otherwise, other
+ * windows will be searched.
+ *
+ * @param aURI
+ * URI to search for
+ * @param aOpenNew
+ * True to open a new tab and switch to it, if no existing tab is found
+ * @param A callback to call when the tab is open, the tab's browser will be
+ * passed as an argument
+ * @return True if a tab was switched to (or opened), false otherwise
+ */
+function switchToTabHavingURI(aURI, aOpenNew, aCallback) {
+ function switchIfURIInWindow(aWindow) {
+ if (!aWindow.gBrowser)
+ return false;
+ let browsers = aWindow.gBrowser.browsers;
+ for (let i = 0; i < browsers.length; i++) {
+ let browser = browsers[i];
+ if (browser.currentURI.equals(aURI)) {
+ // Focus the matching window & tab
+ aWindow.focus();
+ aWindow.gBrowser.tabContainer.selectedIndex = i;
+ if (aCallback)
+ aCallback(browser);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // This can be passed either nsIURI or a string.
+ if (!(aURI instanceof Ci.nsIURI))
+ aURI = Services.io.newURI(aURI);
+
+ // Prioritise this window.
+ if (switchIfURIInWindow(window))
+ return true;
+
+ let winEnum = Services.wm.getEnumerator("navigator:browser");
+ while (winEnum.hasMoreElements()) {
+ let browserWin = winEnum.getNext();
+ // Skip closed (but not yet destroyed) windows,
+ // and the current window (which was checked earlier).
+ if (browserWin.closed || browserWin == window)
+ continue;
+ if (switchIfURIInWindow(browserWin))
+ return true;
+ }
+
+ // No opened tab has that url.
+ if (aOpenNew) {
+ let browserWin = openUILinkIn(aURI.spec, "tabfocused");
+ if (aCallback) {
+ browserWin.addEventListener("pageshow", function browserWinPageShow(event) {
+ if (event.target.location.href != aURI.spec)
+ return;
+ browserWin.removeEventListener("pageshow", browserWinPageShow, true);
+ aCallback(browserWin.getBrowser().selectedBrowser);
+ }, true);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+// Determines if a browser is "empty"
+function isBrowserEmpty(aBrowser) {
+ return aBrowser.sessionHistory.count < 2 &&
+ aBrowser.currentURI.spec == "about:blank" &&
+ !aBrowser.contentDocument.body.hasChildNodes();
+}
+
+function subscribeToFeed(href, event) {
+ // Just load the feed in the content area to either subscribe or show the
+ // preview UI
+ var w = getTopWin();
+ var charset;
+ if (w) {
+ var browser = w.getBrowser();
+ charset = browser.characterSet;
+ } else {
+ // When calling this function without any open navigator window
+ charset = document.characterSet;
+ }
+ let feedURI = makeURI(href, charset);
+
+ openUILink(href, event, false, true);
+}
+
+function subscribeToFeedMiddleClick(href, event) {
+ if (event.button == 1) {
+ this.subscribeToFeed(href, event);
+ // unlike for command events, we have to close the menus manually
+ closeMenus(event.target);
+ }
+}
+
+function OpenSearchEngineManager() {
+ var window = Services.wm.getMostRecentWindow("Browser:SearchManager");
+ if (window)
+ window.focus();
+ else {
+ var arg = { value: false };
+ openDialog("chrome://communicator/content/search/engineManager.xul",
+ "_blank", "chrome,dialog,modal,centerscreen,resizable", arg);
+ if (arg.value)
+ loadAddSearchEngines();
+ }
+}
+
+function loadAddSearchEngines() {
+ var newWindowPref = Services.prefs.getIntPref("browser.link.open_newwindow");
+ var where = newWindowPref == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ? "tabfocused" : "window";
+ var searchEnginesURL = Services.urlFormatter.formatURLPref("browser.search.searchEnginesURL");
+ openUILinkIn(searchEnginesURL, where);
+}
+
+function FillInHTMLTooltip(tipElement)
+{
+ // Don't show the tooltip if the tooltip node is a document or disconnected.
+ if (!tipElement.ownerDocument ||
+ (tipElement.ownerDocument.compareDocumentPosition(tipElement) & document.DOCUMENT_POSITION_DISCONNECTED))
+ return false;
+
+ var defView = tipElement.ownerDocument.defaultView;
+ // XXX Work around bug 350679:
+ // "Tooltips can be fired in documents with no view".
+ if (!defView)
+ return false;
+
+ const XLinkNS = "http://www.w3.org/1999/xlink";
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ var titleText = null;
+ var XLinkTitleText = null;
+ var SVGTitleText = null;
+ var lookingForSVGTitle = true;
+ var direction = defView.getComputedStyle(tipElement, "")
+ .getPropertyValue("direction");
+
+ // If the element is invalid per HTML5 Forms specifications and has no title,
+ // show the constraint validation error message.
+ if ((tipElement instanceof HTMLInputElement ||
+ tipElement instanceof HTMLTextAreaElement ||
+ tipElement instanceof HTMLSelectElement ||
+ tipElement instanceof HTMLButtonElement) &&
+ !tipElement.hasAttribute("title") &&
+ (!tipElement.form || !tipElement.form.noValidate)) {
+ // If the element is barred from constraint validation or is valid,
+ // the validation message will be the empty string.
+ titleText = tipElement.validationMessage || null;
+ }
+
+ while ((titleText == null) && (XLinkTitleText == null) &&
+ (SVGTitleText == null) && tipElement) {
+ if (tipElement.nodeType == Node.ELEMENT_NODE &&
+ tipElement.namespaceURI != XULNS) {
+ titleText = tipElement.getAttribute("title");
+ if ((tipElement instanceof HTMLAnchorElement ||
+ tipElement instanceof HTMLAreaElement ||
+ tipElement instanceof HTMLLinkElement ||
+ tipElement instanceof SVGAElement) && tipElement.href) {
+ XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
+ }
+ if (lookingForSVGTitle &&
+ (!(tipElement instanceof SVGElement) ||
+ tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
+ lookingForSVGTitle = false;
+ }
+ if (lookingForSVGTitle) {
+ let length = tipElement.childNodes.length;
+ for (let i = 0; i < length; i++) {
+ let childNode = tipElement.childNodes[i];
+ if (childNode instanceof SVGTitleElement) {
+ SVGTitleText = childNode.textContent;
+ break;
+ }
+ }
+ }
+ direction = defView.getComputedStyle(tipElement, "")
+ .getPropertyValue("direction");
+ }
+ tipElement = tipElement.parentNode;
+ }
+
+ var tipNode = document.getElementById("aHTMLTooltip");
+ tipNode.style.direction = direction;
+
+ return [titleText, XLinkTitleText, SVGTitleText].some(function (t) {
+ if (t && /\S/.test(t)) {
+ // Make CRLF and CR render one line break each.
+ tipNode.setAttribute("label", t.replace(/\r\n?/g, "\n"));
+ return true;
+ }
+ return false;
+ });
+}
+
+function GetFileFromString(aString)
+{
+ // If empty string just return null.
+ if (!aString)
+ return null;
+
+ let commandLine = Cc["@mozilla.org/toolkit/command-line;1"]
+ .createInstance(Ci.nsICommandLine);
+ let uri = commandLine.resolveURI(aString);
+ return uri instanceof Ci.nsIFileURL ?
+ uri.file.QueryInterface(Ci.nsIFile) : null;
+}
+
+function CopyImage()
+{
+ var param = Cu.createCommandParams();
+ param.setLongValue("imageCopy",
+ Ci.nsIContentViewerEdit.COPY_IMAGE_ALL);
+ document.commandDispatcher.getControllerForCommand("cmd_copyImage")
+ .QueryInterface(Ci.nsICommandController)
+ .doCommandWithParams("cmd_copyImage", param);
+}
+
+/**
+ * Moved from toolkit/content/globalOverlay.js
+ */
+function goSetMenuValue(aCommand, aLabelAttribute) {
+ var commandNode = top.document.getElementById(aCommand);
+ if (commandNode) {
+ var label = commandNode.getAttribute(aLabelAttribute);
+ if (label)
+ commandNode.setAttribute("label", label);
+ }
+}
+
+function goSetAccessKey(aCommand, aValueAttribute) {
+ var commandNode = top.document.getElementById(aCommand);
+ if (commandNode) {
+ var value = commandNode.getAttribute(aValueAttribute);
+ if (value)
+ commandNode.setAttribute("accesskey", value);
+ }
+}
+
+// this function is used to inform all the controllers attached to a node that an event has occurred
+// (e.g. the tree controllers need to be informed of blur events so that they can change some of the
+// menu items back to their default values)
+function goOnEvent(aNode, aEvent) {
+ var numControllers = aNode.controllers.getControllerCount();
+ var controller;
+
+ for (var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++) {
+ controller = aNode.controllers.getControllerAt(controllerIndex);
+ if (controller)
+ controller.onEvent(aEvent);
+ }
+}