summaryrefslogtreecommitdiffstats
path: root/comm/suite/base
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/base')
-rw-r--r--comm/suite/base/content/about.js46
-rw-r--r--comm/suite/base/content/about.xhtml43
-rw-r--r--comm/suite/base/content/aboutLife.xhtml72
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.css12
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.js33
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.xul62
-rw-r--r--comm/suite/base/content/askViewZoom.js43
-rw-r--r--comm/suite/base/content/askViewZoom.xul27
-rw-r--r--comm/suite/base/content/blockedSite.js126
-rw-r--r--comm/suite/base/content/blockedSite.xhtml77
-rw-r--r--comm/suite/base/content/certError.css7
-rw-r--r--comm/suite/base/content/certError.js181
-rw-r--r--comm/suite/base/content/certError.xhtml164
-rw-r--r--comm/suite/base/content/certError.xml18
-rw-r--r--comm/suite/base/content/charsetOverlay.xul24
-rw-r--r--comm/suite/base/content/communicator.css329
-rw-r--r--comm/suite/base/content/contentAreaClick.js260
-rw-r--r--comm/suite/base/content/contentAreaContextOverlay.xul401
-rw-r--r--comm/suite/base/content/defaultClientDialog.js58
-rw-r--r--comm/suite/base/content/defaultClientDialog.xul37
-rw-r--r--comm/suite/base/content/extensionsOverlay.css7
-rw-r--r--comm/suite/base/content/findUtils.js141
-rw-r--r--comm/suite/base/content/fullscreen-video.xhtml156
-rw-r--r--comm/suite/base/content/gopherAddon.xhtml55
-rw-r--r--comm/suite/base/content/helpEditorOverlay.xul54
-rw-r--r--comm/suite/base/content/helpMessengerOverlay.xul57
-rw-r--r--comm/suite/base/content/helpSecurityOverlay.xul137
-rw-r--r--comm/suite/base/content/nsContextMenu.js1676
-rw-r--r--comm/suite/base/content/openLocation.js125
-rw-r--r--comm/suite/base/content/openLocation.xul74
-rw-r--r--comm/suite/base/content/overrides/app-license.html8
-rw-r--r--comm/suite/base/content/safeMode.js92
-rw-r--r--comm/suite/base/content/safeMode.xul58
-rw-r--r--comm/suite/base/content/tasksOverlay.js276
-rw-r--r--comm/suite/base/content/tasksOverlay.xul124
-rw-r--r--comm/suite/base/content/utilityOverlay.js1969
-rw-r--r--comm/suite/base/content/utilityOverlay.xul719
-rw-r--r--comm/suite/base/content/viewApplyThemeOverlay.js170
-rw-r--r--comm/suite/base/content/viewApplyThemeOverlay.xul34
-rw-r--r--comm/suite/base/content/viewSourceOverlay.js32
-rw-r--r--comm/suite/base/content/viewSourceOverlay.xul81
-rw-r--r--comm/suite/base/content/viewZoomOverlay.js479
-rw-r--r--comm/suite/base/content/viewZoomOverlay.xul55
-rw-r--r--comm/suite/base/jar.mn96
-rw-r--r--comm/suite/base/moz.build17
45 files changed, 8712 insertions, 0 deletions
diff --git a/comm/suite/base/content/about.js b/comm/suite/base/content/about.js
new file mode 100644
index 0000000000..15e3ebad4d
--- /dev/null
+++ b/comm/suite/base/content/about.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+window.onload = function () {
+ // get release notes URL and vendor URL from prefs
+ var releaseNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
+ if (releaseNotesURL != "about:blank") {
+ var relnotes = document.getElementById("releaseNotesURL");
+ relnotes.href = releaseNotesURL;
+ }
+
+ var vendorURL = Services.urlFormatter.formatURLPref("app.vendorURL");
+ if (vendorURL != "about:blank") {
+ var vendor = document.getElementById("vendorURL");
+ vendor.href = vendorURL;
+ }
+
+ // append the version of the XUL application (!= XULRunner platform version)
+ var versionNum = AppConstants.MOZ_APP_VERSION_DISPLAY;
+ var version = document.getElementById("version");
+ version.appendChild(document.createTextNode(versionNum));
+
+ // append user agent
+ var ua = navigator.userAgent;
+ if (ua) {
+ var uaItem = document.getElementById("userAgent");
+ uaItem.appendChild(document.createTextNode(ua));
+ uaItem.hidden = false;
+ }
+
+ // append build identifier
+ var buildId = Services.appinfo.appBuildID;
+ if (buildId) {
+ var buildItem = document.getElementById("buildID");
+ buildItem.appendChild(document.createTextNode(buildId));
+ buildItem.hidden = false;
+ }
+
+ // Determine and display current channel.
+ document.getElementById("currentChannel").textContent =
+ Services.prefs.getDefaultBranch("").getCharPref("app.update.channel");
+}
diff --git a/comm/suite/base/content/about.xhtml b/comm/suite/base/content/about.xhtml
new file mode 100644
index 0000000000..7303f040a8
--- /dev/null
+++ b/comm/suite/base/content/about.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+%globalDTD;
+<!ENTITY % suiteAboutDTD SYSTEM "chrome://communicator/locale/about.dtd" >
+%suiteAboutDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>About:</title>
+ <link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
+ <script src="chrome://communicator/content/about.js"/>
+</head>
+
+<body dir="&locale.dir;">
+ <div id="aboutLogoContainer">
+ <a id="vendorURL" href="http://www.seamonkey-project.org/">
+ <img src="about:logo" alt="&brandShortName;"/>
+ <p id="version">&about.version; </p>
+ </a>
+ </div>
+
+ <ul id="aboutPageList">
+ <li>&channel.description.start;<strong id="currentChannel"/>&channel.description.end;</li>
+ <li>&about.credits.beforeLink;<a href="about:credits">&about.credits.linkTitle;</a>&about.credits.afterLink;</li>
+ <li>&about.license.beforeTheLink;<a href="about:license">&about.license.linkTitle;</a>&about.license.afterTheLink;</li>
+ <li>&about.relnotes.beforeTheLink;<a id="releaseNotesURL" href="">&about.relnotes.linkTitle;</a>&about.relnotes.afterTheLink;</li>
+ <li>&about.buildconfig.beforeTheLink;<a href="about:buildconfig">&about.buildconfig.linkTitle;</a>&about.buildconfig.afterTheLink;</li>
+ <li id="userAgent" hidden="true">&about.userAgent;</li>
+ <li id="buildID" hidden="true">&about.buildIdentifier;</li>
+ </ul>
+
+</body>
+</html>
diff --git a/comm/suite/base/content/aboutLife.xhtml b/comm/suite/base/content/aboutLife.xhtml
new file mode 100644
index 0000000000..bd61f40f44
--- /dev/null
+++ b/comm/suite/base/content/aboutLife.xhtml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 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 html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Life, the Universe, and Everything</title>
+
+ <script>
+ function load()
+ {
+ document.getElementById("magic").textContent = Math.sqrt(1764);
+ }
+ </script>
+
+ <style>
+ html {
+ height: 100%;
+ width: 100%;
+ background-color: #B0D8FF;
+ background-image: radial-gradient(circle closest-side, #72B9FF, #B0D8FF);
+ }
+
+ body {
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ display: -moz-box;
+ -moz-box-pack: center;
+ -moz-box-align: center;
+ }
+
+ p:empty {
+ font-size: 16px;
+ color: #72B9FF;
+ text-shadow: none;
+ transform: rotate(0deg);
+ }
+
+ p {
+ font-family: arial;
+ font-weight: bold;
+ margin: 0;
+ transition-property: font-size, color, text-shadow, transform;
+ transition-duration: 6s;
+ transition-timing-function: ease;
+ font-size: 200px;
+ color: #0303E4;
+ text-shadow: #333333 3px 3px 3px;
+ transform: rotate(2160deg);
+ }
+
+ p:hover {
+ transition-duration: 1s;
+ font-size: 300px;
+ color: #2727E8;
+ transform: rotate(2520deg);
+ }
+ </style>
+
+</head>
+
+<body onload="load();">
+ <p id="magic"/>
+</body>
+
+</html>
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.css b/comm/suite/base/content/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..14f610a5e4
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#warningBox:not(.private) .private,
+#warningBox:not(.normal) .normal {
+ display: none;
+}
+
+.title {
+ background-size: auto;
+}
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.js b/comm/suite/base/content/aboutPrivateBrowsing.js
new file mode 100644
index 0000000000..202bc4dbc8
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.js
@@ -0,0 +1,33 @@
+/* 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/. */
+
+window.onload = function () {
+ if (window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing) {
+ document.getElementById("warningBox").className = "private";
+ document.title = document.getElementById("privateTitle").textContent;
+ }
+ else {
+ document.getElementById("warningBox").className = "normal";
+ document.title = document.getElementById("normalTitle").textContent;
+ }
+
+ document.getElementById("learnMoreButton")
+ .addEventListener("command", function() {
+ openHelp("private-browsing",
+ "chrome://communicator/locale/help/suitehelp.rdf");
+ });
+
+ document.getElementById("closeWindowButton")
+ .addEventListener("command", function() {
+ window.close();
+ });
+
+ document.getElementById("privateWindowButton")
+ .addEventListener("command", function() {
+ openNewPrivateWith(location.href);
+ });
+}
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.xul b/comm/suite/base/content/aboutPrivateBrowsing.xul
new file mode 100644
index 0000000000..30361ef38f
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.xul
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/aboutPrivateBrowsing.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/aboutPrivateBrowsing.css" type="text/css"?>
+
+<!DOCTYPE window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % aboutPrivateBrowsingDTD SYSTEM "chrome://communicator/locale/aboutPrivateBrowsing.dtd" >
+ %aboutPrivateBrowsingDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="warningScreen"
+ align="center">
+
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://communicator/content/tasksOverlay.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script src="chrome://communicator/content/aboutPrivateBrowsing.js"/>
+
+ <spacer flex="1"/>
+ <hbox id="warningBox" align="start">
+ <image id="warningBoxIcon"/>
+ <vbox id="warningOuterBox">
+ <vbox id="warningTitle">
+ <label id="privateTitle" class="private">&privatebrowsingpage.title.private;</label>
+ <label id="normalTitle" class="normal">&privatebrowsingpage.title.normal;</label>
+ </vbox>
+ <vbox id="warningStatus">
+ <label class="private">&privatebrowsingpage.status.private;</label>
+ <label class="normal">&privatebrowsingpage.status.normal;</label>
+ </vbox>
+ <vbox id="warningInnerBox" align="start">
+ <description id="warningText">&privatebrowsingpage.common.description;</description>
+ <hbox id="trackWarnBox" class="private">
+ <image id="trackWarnIcon"/>
+ <description flex="1">&privatebrowsingpage.track.warn;</description>
+ </hbox>
+ <button id="learnMoreButton"
+ label="&privatebrowsingpage.learnmore.label;"
+ accesskey="&privatebrowsingpage.learnmore.accesskey;"/>
+ <description class="private">&privatebrowsingpage.close.info;</description>
+ <button id="closeWindowButton"
+ class="private"
+ label="&privatebrowsingpage.close.label;"
+ accesskey="&privatebrowsingpage.close.accesskey;"/>
+ <description class="normal">&privatebrowsingpage.start.info;</description>
+ <button id="privateWindowButton"
+ class="normal"
+ label="&privatebrowsingpage.private.label;"
+ accesskey="&privatebrowsingpage.private.accesskey;"/>
+ </vbox>
+ </vbox>
+ </hbox>
+ <spacer flex="2"/>
+</window>
diff --git a/comm/suite/base/content/askViewZoom.js b/comm/suite/base/content/askViewZoom.js
new file mode 100644
index 0000000000..6a3f903d6b
--- /dev/null
+++ b/comm/suite/base/content/askViewZoom.js
@@ -0,0 +1,43 @@
+/* -*- 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/. */
+
+var dialog;
+var args;
+
+function onLoad() {
+ args = window.arguments[0];
+ args.zoomOK = false;
+
+ dialog = {};
+ dialog.OKButton = document.documentElement.getButton("accept");
+
+ dialog.input = document.getElementById("zoomValue");
+ dialog.input.value = args.value;
+ dialog.input.select();
+ dialog.input.focus();
+
+ doEnabling();
+}
+
+function onAccept() {
+ var zoom = parseFloat(dialog.input.value);
+ if (!isNaN(zoom) && zoom >= args.zoomMin && zoom <= args.zoomMax) {
+ args.value = zoom;
+ args.zoomOK = true;
+ }
+ return args.zoomOK;
+}
+
+function doEnabling() {
+ var enable = false;
+ if (dialog.input.value) {
+ var zoom = parseFloat(dialog.input.value);
+ if (!isNaN(zoom) && zoom >= args.zoomMin && zoom <= args.zoomMax)
+ enable = true;
+ }
+
+ dialog.OKButton.disabled = !enable;
+}
diff --git a/comm/suite/base/content/askViewZoom.xul b/comm/suite/base/content/askViewZoom.xul
new file mode 100644
index 0000000000..5f38270d15
--- /dev/null
+++ b/comm/suite/base/content/askViewZoom.xul
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/askViewZoom.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="askViewZoom"
+ title="&askViewZoom.title;"
+ ondialogaccept="return onAccept();"
+ onload="onLoad();">
+
+ <script src="chrome://communicator/content/askViewZoom.js"/>
+
+ <hbox>
+ <label value="&selectZoom.label;" control="zoomValue"/>
+ </hbox>
+
+ <hbox>
+ <textbox id="zoomValue" oninput="doEnabling();"/>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/base/content/blockedSite.js b/comm/suite/base/content/blockedSite.js
new file mode 100644
index 0000000000..4a3cf0d69e
--- /dev/null
+++ b/comm/suite/base/content/blockedSite.js
@@ -0,0 +1,126 @@
+/* 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/. */
+
+// Error url MUST be formatted like this:
+// about:blocked?e=error_code&u=url
+
+// Note that this file uses document.documentURI to get
+// the URL (with the format from above). This is because
+// document.location.href gets the current URI off the docshell,
+// which is the URL displayed in the location bar, i.e.
+// the URI that the user attempted to load.
+
+// initializing the page in this way, window.onload won't work here
+
+initPage();
+
+function getErrorCode()
+{
+ var url = document.documentURI;
+ var error = url.indexOf("e=");
+ var duffUrl = url.indexOf("&u=");
+ return url.slice(error + 2, duffUrl);
+}
+
+function getURL()
+{
+ var url = document.documentURI;
+ var match = url.match(/&u=([^&]+)&/);
+
+ // match == null if not found; if so, return an empty string
+ // instead of what would turn out to be portions of the URI
+ if (!match)
+ return "";
+
+ url = decodeURIComponent(match[1]);
+
+ // If this is a view-source page, then get then real URI of the page
+ if (url.startsWith("view-source:"))
+ url = url.slice(12);
+ return url;
+}
+
+ /**
+ * Check whether this warning page should be overridable or whether
+ * the "ignore warning" button should be hidden.
+ */
+ function getOverride()
+ {
+ var url = document.documentURI;
+ return /&o=1&/.test(url);
+ }
+
+/**
+ * Attempt to get the hostname via document.location. Fail back
+ * to getURL so that we always return something meaningful.
+ */
+function getHostString()
+{
+ try {
+ return document.location.hostname;
+ } catch (e) {
+ return getURL();
+ }
+}
+
+function deleteElement(element) {
+ var el = document.getElementById(element);
+ if (el)
+ el.remove();
+}
+
+function initPage()
+{
+ // Handoff to the appropriate initializer, based on error code
+ var error = "";
+ switch (getErrorCode()) {
+ case "malwareBlocked":
+ error = "malware";
+ break;
+ case "deceptiveBlocked":
+ error = "phishing";
+ break;
+ case "unwantedBlocked":
+ error = "unwanted";
+ break;
+ case "harmfulBlocked":
+ error = "harmful";
+ break;
+ default:
+ return;
+ }
+
+ if (error != "malware") {
+ deleteElement("errorTitleText_malware");
+ deleteElement("errorShortDescText_malware");
+ deleteElement("errorLongDescText_malware");
+ }
+
+ if (error != "phishing") {
+ deleteElement("errorTitleText_phishing");
+ deleteElement("errorShortDescText_phishing");
+ deleteElement("errorLongDescText_phishing");
+ }
+
+ if (error != "unwanted") {
+ deleteElement("errorTitleText_unwanted");
+ deleteElement("errorShortDescText_unwanted");
+ deleteElement("errorLongDescText_unwanted");
+ }
+
+ if (error != "harmful") {
+ deleteElement("errorTitleText_harmful");
+ deleteElement("errorShortDescText_harmful");
+ deleteElement("errorLongDescText_harmful");
+ }
+
+ // Set sitename
+ document.getElementById(error + "_sitename").textContent = getHostString();
+ document.title = document.getElementById("errorTitleText_" + error)
+ .innerHTML;
+
+ if (!getOverride())
+ deleteElement("ignoreWarningButton");
+}
+
diff --git a/comm/suite/base/content/blockedSite.xhtml b/comm/suite/base/content/blockedSite.xhtml
new file mode 100644
index 0000000000..40cce3eb49
--- /dev/null
+++ b/comm/suite/base/content/blockedSite.xhtml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % blockedSiteDTD SYSTEM "chrome://communicator/locale/safeBrowsing.dtd">
+ %blockedSiteDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml" class="blacklist">
+ <head>
+ <link rel="stylesheet" href="chrome://communicator/content/certError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://communicator/skin/blockedSite.css" type="text/css" media="all" />
+ <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>
+ </head>
+
+ <body dir="&locale.dir;">
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText_phishing">&safeb.blocked.phishingPage.title2;</h1>
+ <h1 id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
+ <h1 id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
+ <h1 id="errorTitleText_harmful">&safeb.blocked.harmfulPage.title;</h1>
+ </div>
+
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc2;</p>
+ <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
+ <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
+ <p id="errorShortDescText_harmful">&safeb.blocked.harmfulPage.shortDesc;</p>
+ </div>
+
+ <!-- Long Description -->
+ <div id="errorLongDesc">
+ <p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc2;</p>
+ <p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
+ <p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
+ <p id="errorLongDescText_harmful">&safeb.blocked.harmfulPage.longDesc;</p>
+ </div>
+
+ <!-- Action buttons -->
+ <div id="buttons">
+ <!-- Commands handled in utilityOverlay.js -->
+ <span id="getMeOutOfHereButton"
+ class="button"
+ label="&safeb.palm.accept.label;"/>
+ <span id="reportButton"
+ class="button"
+ label="&safeb.palm.reportPage.label;"/>
+ <span id="ignoreWarningButton"
+ class="button"
+ label="&safeb.palm.decline.label;"/>
+ </div>
+ </div>
+ </div>
+ <!--
+ - Note: It is important to run the script this way, instead of using
+ - a window.onload function. This is because error pages are loaded as
+ - LOAD_BACKGROUND, which means that onload handlers will not be executed.
+ -->
+ <script src="chrome://communicator/content/blockedSite.js"/>
+ </body>
+</html>
diff --git a/comm/suite/base/content/certError.css b/comm/suite/base/content/certError.css
new file mode 100644
index 0000000000..93dc0a4cf0
--- /dev/null
+++ b/comm/suite/base/content/certError.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+.button {
+ -moz-binding: url("chrome://communicator/content/certError.xml#button");
+}
diff --git a/comm/suite/base/content/certError.js b/comm/suite/base/content/certError.js
new file mode 100644
index 0000000000..96a89b0685
--- /dev/null
+++ b/comm/suite/base/content/certError.js
@@ -0,0 +1,181 @@
+/* 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/. */
+
+// The following parameters are parsed from the error URL:
+// e - the error code
+// s - custom CSS class to allow alternate styling/favicons
+// d - error description
+
+// Note that this file uses document.documentURI to get
+// the URL (with the format from above). This is because
+// document.location.href gets the current URI off the docshell,
+// which is the URL displayed in the location bar, i.e.
+// the URI that the user attempted to load.
+
+// setting up the event listeners and initializing the page
+// in this way given that window.onload won't work here
+
+document.getElementById("technicalContentHeading")
+ .addEventListener("click", function() { toggle("technicalContent"); });
+
+document.getElementById("expertContentHeading")
+ .addEventListener("click", function() { toggle("expertContent"); });
+
+let gSearchParams;
+
+// Set to true on init if the error code is nssBadCert.
+let gIsCertError;
+
+// Set to true on init if a neterror.
+let gIsNetError;
+
+initPage();
+
+function retryThis(buttonEl) {
+ // Note: The application may wish to handle switching off "offline mode"
+ // before this event handler runs, but using a capturing event handler.
+
+ // Session history has the URL of the page that failed
+ // to load, not the one of the error page. So, just call
+ // reload(), which will also repost POST data correctly.
+ try {
+ location.reload();
+ } catch (e) {
+ // We probably tried to reload a URI that caused an exception to
+ // occur; e.g. a nonexistent file.
+ }
+
+ buttonEl.disabled = true;
+}
+
+function initPage() {
+ gSearchParams = new URLSearchParams(document.documentURI.split("?")[1]);
+
+ let err = gSearchParams.get("e");
+ // List of neterror pages which have no error code and
+ // could have an illustration instead.
+ let illustratedErrors = [
+ "malformedURI", "dnsNotFound", "connectionFailure", "netInterrupt",
+ "netTimeout", "netReset", "netOffline",
+ ];
+ if (illustratedErrors.includes(err)) {
+ document.body.classList.add("illustrated", err);
+ }
+
+ gIsCertError = (err == "nssBadCert");
+ gIsNetError = (document.documentURI.startsWith("about:neterror"));
+
+ let pageTitle = document.getElementById("ept_" + err);
+ if (pageTitle) {
+ document.title = pageTitle.textContent;
+ }
+
+ // If it's an unknown error or there's no title or description defined,
+ // get the generic message.
+ let errTitle = document.getElementById("et_" + err);
+ let errDesc = document.getElementById("ed_" + err);
+ if (!errTitle || !errDesc) {
+ errTitle = document.getElementById("et_generic");
+ errDesc = document.getElementById("ed_generic");
+ }
+
+ let title = document.getElementById("errorTitleText");
+ if (title) {
+ title.innerHTML = errTitle.innerHTML;
+ }
+
+ let sd = document.getElementById("errorShortDescText");
+ if (sd) {
+ if (gIsCertError) {
+ sd.innerHTML = errDesc.innerHTML;
+ } else if (!err || err == "unknownProtocolFound") {
+ sd.remove();
+ }
+ }
+
+ let xd = document.getElementById("errorShortDescExtra");
+ if (xd) {
+ let errExtra = document.getElementById("ex_" + err);
+ if (gIsCertError && errExtra) {
+ xd.innerHTML = errExtra.innerHTML;
+ } else {
+ xd.remove();
+ }
+ }
+
+ let ld = document.getElementById("errorLongDesc");
+ if (ld && !gIsCertError) {
+ ld.innerHTML = errDesc.innerHTML;
+ }
+
+ // Remove undisplayed errors to avoid bug 39098.
+ let errContainer = document.getElementById("errorContainer");
+ errContainer.remove();
+
+ if (gIsCertError || err == "inadequateSecurityError") {
+ for (let host of document.querySelectorAll(".hostname")) {
+ host.textContent = location.host;
+ }
+ }
+
+ if (gIsCertError || err == "sslv3Used") {
+ document.body.classList.add("certerror");
+ }
+
+ if (gIsCertError || err == "remoteXUL" || err == "cspBlocked" ||
+ err == "inadequateSecurityError") {
+ // Remove the "Try again" button for certificate errors, remote XUL errors,
+ // CSP violations (Bug 553180) and HTTP/2 inadequate security,
+ // given that it is useless.
+ document.getElementById("netErrorButtonContainer").style.display = "none";
+ }
+
+ let className = gSearchParams.get("s");
+ if (className && className != "expertBadCert") {
+ // Associate a CSS class with the root of the page, if one was passed in,
+ // to allow custom styling.
+ // Not "expertBadCert" though, don't want to deal with the favicon
+ document.documentElement.classList.add(className);
+ }
+
+ if (className == "expertBadCert") {
+ toggle("technicalContent");
+ toggle("expertContent");
+ }
+
+ // Disallow overrides if this is a Strict-Transport-Security
+ // host and the cert is bad (STS Spec section 7.3);
+ // or if the cert error is in a frame (bug 633691).
+ if (className == "badStsCert" || window != top || !gIsCertError) {
+ let expertContent = document.getElementById("expertContent");
+ expertContent.remove();
+ }
+ if (className == "badStsCert") {
+ document.getElementById("badStsCertExplanation").removeAttribute("hidden");
+ }
+
+ // For neterrors set a suitable class.
+ if (gIsNetError) {
+ document.body.classList.add("neterror");
+ }
+
+ // For neterrors and null error codes do not show the What Should I Do and
+ // Technical Details sections.
+ if (gIsNetError || !err) {
+ let whatShould = document.getElementById("whatShouldIDoContent");
+ whatShould.remove();
+ let technicalContent = document.getElementById("technicalContent");
+ technicalContent.remove();
+ }
+ var event = new CustomEvent("AboutNetAndCertErrorLoad", {bubbles: true});
+ document.dispatchEvent(event);
+}
+
+function toggle(id) {
+ var el = document.getElementById(id);
+ if (el.hasAttribute("collapsed"))
+ el.removeAttribute("collapsed");
+ else
+ el.setAttribute("collapsed", true);
+}
diff --git a/comm/suite/base/content/certError.xhtml b/comm/suite/base/content/certError.xhtml
new file mode 100644
index 0000000000..e63f5d1844
--- /dev/null
+++ b/comm/suite/base/content/certError.xhtml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % htmlDTD
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
+ %netErrorDTD;
+ <!ENTITY % globalDTD
+ SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % certerrorDTD
+ SYSTEM "chrome://communicator/locale/certError.dtd">
+ %certerrorDTD;
+]>
+
+<!-- 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/. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&loadError.label;</title>
+ <link rel="stylesheet" href="chrome://communicator/content/certError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://communicator/skin/certError.css" type="text/css" media="all" />
+ <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
+ </head>
+
+ <body dir="&locale.dir;">
+
+ <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
+ <div id="errorContainer" style="display: none;">
+ <div id="errorPageTitlesContainer">
+ <span id="ept_nssBadCert">&certerror.pagetitle;</span>
+ <span id="ept_dnsNotFound">&dnsNotFound.pageTitle;</span>
+ <span id="ept_malformedURI">&malformedURI.pageTitle;</span>
+ </div>
+ <div id="errorTitlesContainer">
+ <h1 id="et_generic">&generic.title;</h1>
+ <h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
+ <h1 id="et_fileNotFound">&fileNotFound.title;</h1>
+ <h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
+ <h1 id="et_malformedURI">&malformedURI.title;</h1>
+ <h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
+ <h1 id="et_connectionFailure">&connectionFailure.title;</h1>
+ <h1 id="et_netTimeout">&netTimeout.title;</h1>
+ <h1 id="et_redirectLoop">&redirectLoop.title;</h1>
+ <h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
+ <h1 id="et_netReset">&netReset.title;</h1>
+ <h1 id="et_notCached">&notCached.title;</h1>
+ <h1 id="et_netOffline">&netOffline.title;</h1>
+ <h1 id="et_netInterrupt">&netInterrupt.title;</h1>
+ <h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
+ <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
+ <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
+ <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
+ <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
+ <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
+ <h1 id="et_nssBadCert">&certerror.longpagetitle;</h1>
+ <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
+ <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
+ <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
+ <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
+ </div>
+ <div id="errorDescriptionsContainer">
+ <div id="ed_generic">&generic.longDesc;</div>
+ <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
+ <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
+ <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
+ <div id="ed_malformedURI">&malformedURI.longDesc;</div>
+ <div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
+ <div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
+ <div id="ed_netTimeout">&netTimeout.longDesc;</div>
+ <div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
+ <div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
+ <div id="ed_netReset">&netReset.longDesc;</div>
+ <div id="ed_notCached">&notCached.longDesc;</div>
+ <div id="ed_netOffline">&netOffline.longDesc2;</div>
+ <div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
+ <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
+ <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
+ <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
+ <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
+ <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
+ <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
+ <div id="ed_nssBadCert">&certerror.introPara1a;</div>
+ <div id="ex_nssBadCert">&certerror.introPara2;</div>
+ <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
+ <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
+ <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
+ <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
+ </div>
+ </div>
+
+ <!-- PAGE CONTAINER (for styling purposes only) -->
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText"/>
+ </div>
+
+ <!-- LONG CONTENT (the section most likely to require scrolling) -->
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText"/>
+ <p id="errorShortDescExtra"/>
+ </div>
+
+ <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
+ <div id="errorLongDesc"/>
+
+ <div id="whatShouldIDoContent">
+ <h2>&certerror.whatShouldIDo.heading;</h2>
+ <div id="whatShouldIDoContentText">
+ <p>&certerror.whatShouldIDo.content;</p>
+ <p id="badStsCertExplanation"
+ hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>
+ <span id="getMeOutOfHereButton"
+ class="button"
+ label="&certerror.getMeOutOfHere.label;"/>
+ </div>
+ </div>
+
+ <!-- The following sections can be unhidden by default by setting the
+ "browser.xul.error_pages.expert_bad_cert" pref to true -->
+ <div id="technicalContent" collapsed="true">
+ <h2 id="technicalContentHeading">&certerror.technical.heading;</h2>
+ <p id="technicalContentText"/>
+ </div>
+
+ <div id="expertContent" collapsed="true">
+ <h2 id="expertContentHeading">&certerror.expert.heading;</h2>
+ <div>
+ <p>&certerror.expert.content;</p>
+ <p>&certerror.expert.contentPara2;</p>
+ <span id="exceptionDialogButton"
+ class="button"
+ label="&certerror.addException.label;"/>
+ </div>
+ </div>
+
+ <div id="netErrorButtonContainer" class="button-container">
+ <button id="errorTryAgain"
+ class="primary"
+ autocomplete="off"
+ onclick="retryThis(this);">&retry.label;</button>
+ </div>
+ </div>
+ </div>
+
+ <!--
+ - Note: It is important to run the script this way, instead of using
+ - a window.onload function. This is because error pages are loaded as
+ - LOAD_BACKGROUND, which means that onload handlers will not be executed.
+ -->
+ <script src="chrome://communicator/content/certError.js"/>
+
+ </body>
+</html>
diff --git a/comm/suite/base/content/certError.xml b/comm/suite/base/content/certError.xml
new file mode 100644
index 0000000000..c4fbe12f01
--- /dev/null
+++ b/comm/suite/base/content/certError.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<bindings id="certErrorBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="button" bindToUntrustedContent="true">
+ <content>
+ <xul:button xbl:inherits="anonid=id,label"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/base/content/charsetOverlay.xul b/comm/suite/base/content/charsetOverlay.xul
new file mode 100644
index 0000000000..6f318a1d6b
--- /dev/null
+++ b/comm/suite/base/content/charsetOverlay.xul
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- 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 overlay SYSTEM "chrome://global/locale/charsetMenu.dtd">
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script>
+ ChromeUtils.import("resource://gre/modules/CharsetMenu.jsm");
+
+ function UpdateCharsetMenu(aCharset, aNode)
+ {
+ var bundle = document.getElementById("charsetBundle");
+ CharsetMenu.update(aNode, bundle.getString(aCharset.toLowerCase()));
+ }
+ </script>
+
+ <menu id="charsetMenu"
+ label="&charsetMenu2.label;"
+ accesskey="&charsetMenu2.accesskey;">
+ <menupopup id="charsetPopup"
+ onpopupshowing="CharsetMenu.build(this, true, this.getAttribute('detectors') != 'false');"/>
+ </menu>
+</overlay>
diff --git a/comm/suite/base/content/communicator.css b/comm/suite/base/content/communicator.css
new file mode 100644
index 0000000000..6ec458f0df
--- /dev/null
+++ b/comm/suite/base/content/communicator.css
@@ -0,0 +1,329 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: print preview toolbar ::::: */
+
+toolbar[printpreview="true"] {
+ -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
+}
+
+/* ::::: autocomplete textbox ::::: */
+
+textbox[type="autocomplete"] {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete");
+}
+
+panel[type="autocomplete"] {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-result-popup");
+}
+
+.autocomplete-history-popup {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-history-popup");
+}
+
+.autocomplete-treebody {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-treebody");
+}
+
+panel[type="autocomplete-richlistbox"] {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup");
+}
+
+.autocomplete-richlistbox {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistbox");
+ -moz-user-focus: ignore;
+}
+
+.autocomplete-richlistbox > scrollbox {
+ overflow-x: hidden !important;
+}
+
+.autocomplete-history-dropmarker {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#history-dropmarker");
+}
+
+.autocomplete-richlistitem {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem");
+ -moz-box-orient: vertical;
+ overflow: -moz-hidden-unscrollable;
+}
+
+/* ::::: notification box ::::: */
+
+.browser-notificationbox {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#browser-notificationbox");
+}
+
+.browser-notificationbox[popupnotification="true"] {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#popup-notification");
+}
+
+notification[value="addon-install-started"] {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#addon-progress-notification");
+}
+
+/* ::::: toolbaritem ::::: */
+toolbaritem {
+ -moz-binding: url("chrome://global/content/bindings/general.xml#basecontrol");
+}
+
+/* With the move to the new toolkit, SeaMonkey needs to overwrite certain bindings
+ * if it wants to keep its distinctive likeness. The now hidden new toolkit bindings
+ * will stay accessible via a set xpfe="false" attribute, though, where necessary.
+ */
+
+/******* toolkit access layer *******/
+/* These rules reintroduce the toolkit bindings overwritten later below */
+toolbox[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbox");
+}
+
+toolbox[xpfe="false"] > toolbar,
+toolbar[xpfe="false"][type="menubar"],
+toolbar[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbar");
+}
+
+menubar[xpfe="false"],
+toolbar > toolbaritem > menubar,
+toolbar > menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#menubar");
+}
+
+toolbar > toolbarpaletteitem > toolbaritem > menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#menubar") !important;
+}
+
+.menubar-items {
+ -moz-box-orient: vertical; /* for flex hack */
+}
+
+.menubar-items > menubar {
+ -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
+}
+
+prefwindow,
+prefwindow:root,
+prefpane {
+ -moz-box-orient: vertical;
+}
+
+prefwindow[type="child"] > .paneDeckContainer {
+ overflow: -moz-hidden-unscrollable;
+}
+
+prefwindow[type="child"] > prefpane > .content-box {
+ -moz-box-flex: 1;
+ overflow: -moz-hidden-unscrollable;
+}
+
+preferences {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#preferences");
+ visibility: collapse;
+}
+
+preference {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#preference");
+ visibility: collapse;
+}
+
+radio[pane] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#panebutton") !important;
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+}
+
+prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar {
+ display: none;
+}
+
+prefwindow[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#prefwindow");
+}
+
+prefpane[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#prefpane");
+}
+
+findbar[xpfe="false"] {
+ -moz-binding: url("chrome://global/content/bindings/findbar.xml#findbar");
+}
+
+prefwindow[xpfe="false"] > .paneDeckContainer,
+prefpane[xpfe="false"] > .content-box {
+ overflow: hidden;
+}
+
+/******* SeaMonkey XPFE *******/
+/* These bindings reflect SeaMonkey XPFE, modulo new toolkit features. */
+toolbox {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbox");
+}
+
+toolbar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar");
+}
+
+toolbar[type="menubar"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-menubar");
+}
+
+toolbargrippy {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#toolbargrippy");
+}
+
+menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippymenubar");
+}
+
+prefwindow {
+ -moz-binding: url("chrome://communicator/content/bindings/prefwindow.xml#prefwindow");
+}
+
+prefpane {
+ -moz-binding: url("chrome://communicator/content/bindings/prefwindow.xml#prefpane");
+}
+
+findbar {
+ -moz-binding: url("chrome://communicator/content/bindings/findbar.xml#findbar");
+}
+
+prefwindow > .paneDeckContainer,
+prefpane > .content-box {
+ overflow: visible;
+}
+
+prefwindow[overflow="auto"] > .paneDeckContainer,
+prefwindow[overflow="auto"] prefpane > .content-box {
+ overflow: auto;
+}
+
+dialogheader {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#dialogheader");
+}
+
+%ifndef MOZ_WIDGET_GTK
+statusbar:not([nowindowdrag="true"]) {
+ -moz-window-dragging: drag;
+}
+%endif
+
+%ifdef XP_MACOSX
+.statusbar-resizerpanel {
+ display: none;
+}
+%else
+window[sizemode="maximized"] statusbarpanel.statusbar-resizerpanel {
+ visibility: collapse;
+}
+%endif
+
+statusbar {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#statusbar");
+%ifdef XP_MACOSX
+ padding-right: 14px;
+%endif
+}
+
+statusbarpanel {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#statusbarpanel");
+}
+
+.statusbarpanel-iconic {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-iconic");
+}
+
+.statusbarpanel-iconic-text {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-iconic-text");
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-backgroundbox");
+}
+
+textbox[enablehistory="true"] > .autocomplete-history-dropmarker {
+ display: -moz-box;
+}
+
+/******* lightweight themes *******/
+:root:-moz-lwtheme {
+ color: var(--lwt-text-color) !important
+}
+
+/**
+ * [customization-lwtheme] may or may not be used yet; we leave it in
+ * in case it moves to toolkit in the future.
+ */
+:root:-moz-lwtheme:not([customization-lwtheme]) {
+ background-color: var(--lwt-accent-color) !important;
+ background-image: var(--lwt-header-image) !important;
+}
+
+window[lwtheme="true"] {
+ background-repeat: no-repeat;
+ background-position: top right;
+ background-image: var(--lwt-header-image);
+}
+
+:root[lwthemefooter="true"] #status-bar:-moz-lwtheme {
+ background-repeat: no-repeat;
+ background-position: bottom left;
+ background-color: var(--lwt-accent-color);
+ background-image: var(--lwt-header-image);
+}
+
+/******* sync *******/
+#sync-notifications {
+ -moz-binding: url("chrome://communicator/content/sync/syncNotification.xml#notificationbox");
+ overflow-y: visible !important;
+}
+
+#sync-notifications > notification {
+ -moz-binding: url("chrome://communicator/content/sync/syncNotification.xml#notification");
+}
+
+/******* autohide toolbars *******/
+
+toolbar[type="menubar"][autohide="true"]
+{
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbar-menubar-autohide");
+ overflow: hidden;
+}
+
+toolbar[type="menubar"][autohide="true"][inactive="true"]
+{
+ min-height: 0px !important;
+ height: 0px !important;
+ -moz-appearance: none !important;
+ border-style: none !important;
+}
+
+/******* datepicker *******/
+datepicker {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker");
+}
+
+datepicker[type="popup"] {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker-popup");
+}
+
+datepicker[type="grid"] {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker-grid");
+}
+
+/******* numberbox *******/
+textbox[type="number"] {
+ -moz-binding: url("chrome://communicator/content/bindings/numberbox.xml#numberbox");
+}
+
+/******* spinbuttons *******/
+spinbuttons {
+ -moz-binding: url("chrome://communicator/content/bindings/spinbuttons.xml#spinbuttons");
+}
+
+.spinbuttons-button {
+ -moz-user-focus: ignore;
+}
diff --git a/comm/suite/base/content/contentAreaClick.js b/comm/suite/base/content/contentAreaClick.js
new file mode 100644
index 0000000000..b4c83e991e
--- /dev/null
+++ b/comm/suite/base/content/contentAreaClick.js
@@ -0,0 +1,260 @@
+// /* -*- 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/. */
+
+/*
+ * - [ Dependencies ] ---------------------------------------------------------
+ * utilityOverlay.js:
+ * - gatherTextUnder
+ */
+
+ function hrefAndLinkNodeForClickEvent(event)
+ {
+ var href = "";
+ var isKeyCommand = (event.type == "command");
+ var linkNode = isKeyCommand ? document.commandDispatcher.focusedElement
+ : event.originalTarget;
+
+ while (linkNode instanceof Element) {
+ if (linkNode instanceof HTMLAnchorElement ||
+ linkNode instanceof HTMLAreaElement ||
+ linkNode instanceof HTMLLinkElement) {
+ href = linkNode.href;
+ if (href)
+ break;
+ }
+ // Try MathML href
+ else if (linkNode.namespaceURI == "http://www.w3.org/1998/Math/MathML" &&
+ linkNode.hasAttribute("href")) {
+ href = linkNode.getAttribute("href");
+ href = makeURLAbsolute(linkNode.baseURI, href);
+ break;
+ }
+ // Try simple XLink
+ else if (linkNode.hasAttributeNS("http://www.w3.org/1999/xlink", "href")) {
+ href = linkNode.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+ href = makeURLAbsolute(linkNode.baseURI, href);
+ break;
+ }
+ linkNode = linkNode.parentNode;
+ }
+
+ return href ? {href: href, linkNode: linkNode} : null;
+ }
+
+ // Called whenever the user clicks in the content area,
+ // except when left-clicking on links (special case)
+ // should always return true for click to go through
+ function contentAreaClick(event)
+ {
+ if (!event.isTrusted || event.defaultPrevented) {
+ return true;
+ }
+
+ var isKeyCommand = (event.type == "command");
+ var ceParams = hrefAndLinkNodeForClickEvent(event);
+ if (ceParams) {
+ var href = ceParams.href;
+ if (isKeyCommand) {
+ var doc = event.target.ownerDocument;
+ urlSecurityCheck(href, doc.nodePrincipal);
+ openLinkIn(href, event && event.altKey ? "tabshifted" : "tab",
+ { charset: doc.characterSet,
+ referrerURI: doc.documentURIObject });
+ event.stopPropagation();
+ }
+ else {
+ // if in mailnews block the link left click if we determine
+ // that this URL is phishy (i.e. a potential email scam)
+ if ("gMessengerBundle" in this && event.button < 2 &&
+ isPhishingURL(ceParams.linkNode, false, href))
+ return false;
+ handleLinkClick(event, href, ceParams.linkNode);
+
+ // Mark the page as a user followed link. This is done so that history can
+ // distinguish automatic embed visits from user activated ones. For example
+ // pages loaded in frames are embed visits and lost with the session, while
+ // visits across frames should be preserved.
+ try {
+ PlacesUIUtils.markPageAsFollowedLink(href);
+ } catch (ex) { /* Skip invalid URIs. */ }
+ }
+ return true;
+ }
+
+ if (!isKeyCommand && event.button == 1 &&
+ Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
+ !Services.prefs.getBoolPref("general.autoScroll")) {
+ middleMousePaste(event);
+ }
+
+ return true;
+ }
+
+function handleLinkClick(event, href, linkNode) {
+ if (event.button == 2) // right click
+ return false;
+
+ var where = whereToOpenLink(event);
+ if (where == "current")
+ return false;
+
+ var doc = event.target.ownerDocument;
+
+ if (where == "save") {
+ saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, false,
+ true, doc.documentURIObject, doc);
+ event.preventDefault();
+ return true;
+ }
+
+ var referrerURI = doc.documentURIObject;
+ // if the mixedContentChannel is present and the referring URI passes
+ // a same origin check with the target URI, we can preserve the users
+ // decision of disabling MCB on a page for it's child tabs.
+ var persistAllowMixedContentInChildTab = false;
+
+ if (where == "tab" && getBrowser().docShell.mixedContentChannel) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ var targetURI = makeURI(href);
+ sm.checkSameOriginURI(referrerURI, targetURI, false);
+ persistAllowMixedContentInChildTab = true;
+ }
+ catch (e) { }
+ }
+
+ urlSecurityCheck(href, doc.nodePrincipal);
+ let params = {
+ charset: doc.characterSet,
+ private: gPrivate ? true : false,
+ allowMixedContent: persistAllowMixedContentInChildTab,
+ referrerURI: referrerURI,
+ noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
+ originPrincipal: doc.nodePrincipal,
+ triggeringPrincipal: doc.nodePrincipal,
+ };
+
+ // The new tab/window must use the same userContextId
+ if (doc.nodePrincipal.originAttributes.userContextId) {
+ params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
+ }
+
+ openLinkIn(href, where, params);
+ event.preventDefault();
+ return true;
+}
+
+ function middleMousePaste(event) {
+
+ let clipboard = readFromClipboard();
+
+ if (!clipboard)
+ return;
+
+ // Strip embedded newlines and surrounding whitespace, to match the URL
+ // bar's behavior (stripsurroundingwhitespace).
+ clipboard = clipboard.replace(/\s*\n\s*/g, "");
+
+ clipboard = stripUnsafeProtocolOnPaste(clipboard);
+
+ // If its not the current tab, we don't need to do anything because the
+ // browser doesn't exist.
+ let where = whereToOpenLink(event, true, false);
+ let lastLocationChange;
+ if (where == "current") {
+ lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
+ }
+
+ getShortcutOrURIAndPostData(clipboard).then(data => {
+ try {
+ makeURI(data.url);
+ } catch (ex) {
+ // Not a valid URI.
+ return;
+ }
+
+ try {
+ addToUrlbarHistory(data.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);
+ }
+
+ if (where != "current" ||
+ lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
+ openUILink(data.url, event,
+ { ignoreButton: true,
+ disallowInheritPrincipal: !data.mayInheritPrincipal });
+ }
+ });
+
+ event.stopPropagation();
+ }
+
+ function stripUnsafeProtocolOnPaste(pasteData) {
+ // Don't allow pasting javascript URIs since we don't support
+ // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL for those.
+ let changed = false;
+ let pasteDataNoJS = pasteData.replace(/\r?\n/g, "")
+ .replace(/^(?:\s*javascript:)+/i,
+ () => { changed = true;
+ return ""; });
+ return changed ? pasteDataNoJS : pasteData;
+ }
+
+ function addToUrlbarHistory(aUrlToAdd)
+ {
+ if (gPrivate)
+ return;
+
+ if (!Services.prefs.getBoolPref("browser.urlbar.historyEnabled"))
+ return;
+
+ // Remove leading and trailing spaces first.
+ aUrlToAdd = aUrlToAdd.trim();
+
+ if (!aUrlToAdd)
+ return;
+ // Don't store bad URLs.
+ if (aUrlToAdd.search(/[\x00-\x1F]/) != -1)
+ return;
+
+ getShortcutOrURIAndPostData(aUrlToAdd).then(data => {
+ var fixedUpURI = Services.uriFixup.createFixupURI(data.url, 0);
+ if (!fixedUpURI.schemeIs("data"))
+ PlacesUtils.history.markPageAsTyped(fixedUpURI);
+ }).catch(() => {});
+
+ // Open or create the urlbar history database.
+ var file = GetUrlbarHistoryFile();
+ var connection = Services.storage.openDatabase(file);
+ connection.beginTransaction();
+ if (!connection.tableExists("urlbarhistory"))
+ connection.createTable("urlbarhistory", "url TEXT");
+
+ // If the URL is already present in the database then remove it from
+ // its current position. It is then reinserted at the top of the list.
+ var statement = connection.createStatement(
+ "DELETE FROM urlbarhistory WHERE LOWER(url) = LOWER(?1)");
+ statement.bindByIndex(0, aUrlToAdd);
+ statement.execute();
+ statement.finalize();
+
+ // Put the value as it was typed by the user in to urlbar history.
+ statement = connection.createStatement(
+ "INSERT INTO urlbarhistory (url) VALUES (?1)");
+ statement.bindByIndex(0, aUrlToAdd);
+ statement.execute();
+ statement.finalize();
+
+ // Remove any expired history items so that we don't let
+ // this grow without bound.
+ connection.executeSimpleSQL(
+ "DELETE FROM urlbarhistory WHERE ROWID NOT IN " +
+ "(SELECT ROWID FROM urlbarhistory ORDER BY ROWID DESC LIMIT 30)");
+ connection.commitTransaction();
+ connection.close();
+ }
diff --git a/comm/suite/base/content/contentAreaContextOverlay.xul b/comm/suite/base/content/contentAreaContextOverlay.xul
new file mode 100644
index 0000000000..57601b7d94
--- /dev/null
+++ b/comm/suite/base/content/contentAreaContextOverlay.xul
@@ -0,0 +1,401 @@
+<?xml version="1.0"?>
+<!-- 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 overlay SYSTEM "chrome://communicator/locale/contentAreaCommands.dtd" >
+<overlay id="contentAreaContextOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- Context menu -->
+ <script>
+ // Global variable that holds the nsContextMenu instance.
+ var gContextMenu = null;
+ </script>
+ <script src="chrome://communicator/content/nsContextMenu.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+
+ <stringbundleset>
+ <stringbundle id="contentAreaCommandsBundle"
+ src="chrome://communicator/locale/contentAreaCommands.properties"/>
+ </stringbundleset>
+
+ <popupset id="contentAreaContextSet">
+ <!-- This is a generic context menu for a content area. It contains
+ each and every possible menu choice. The appropriate items are
+ hidden/shown upon display, based on what the user has clicked-on.
+ -->
+ <menupopup id="contentAreaContextMenu"
+ pagemenu="start"
+ onpopupshowing="if (event.target != this) return true; gContextMenu = new nsContextMenu(this, event.shiftKey, event); return gContextMenu.shouldDisplay;"
+ onpopuphiding="if (event.target != this) return; gContextMenu.hiding(); gContextMenu = null;">
+ <menuseparator id="page-menu-separator"/>
+
+ <menuitem id="spell-no-suggestions"
+ disabled="true"
+ label="&spellNoSuggestions.label;"/>
+ <menuseparator id="spell-add-separator"/>
+ <menuitem id="spell-add-to-dictionary"
+ label="&spellAddToDictionary.label;"
+ accesskey="&spellAddToDictionary.accesskey;"
+ oncommand="InlineSpellCheckerUI.addToDictionary();"/>
+ <menuitem id="spell-undo-add-to-dictionary"
+ label="&spellUndoAddToDictionary.label;"
+ accesskey="&spellUndoAddToDictionary.accesskey;"
+ oncommand="InlineSpellCheckerUI.undoAddToDictionary();"/>
+ <menuitem id="spell-ignore-word"
+ label="&spellIgnoreWord.label;"
+ accesskey="&spellIgnoreWord.accesskey;"
+ oncommand="InlineSpellCheckerUI.ignoreWord();"/>
+ <menuseparator id="spell-suggestions-separator"/>
+ <menuitem id="context-openlinkintab"
+ label="&openLinkCmdInTab.label;"
+ accesskey="&openLinkCmdInTab.accesskey;"
+ usercontextid="0"
+ oncommand="gContextMenu.openLinkInTab(event);"/>
+ <menuitem id="context-openlink"
+ label="&openLinkCmd.label;"
+ accesskey="&openLinkCmd.accesskey;"
+ oncommand="gContextMenu.openLinkInWindow();"/>
+ <menuitem id="context-openlinkinprivatewindow"
+ label="&openLinkCmdInPrivateWindow.label;"
+ accesskey="&openLinkCmdInPrivateWindow.accesskey;"
+ oncommand="gContextMenu.openLinkInPrivateWindow();"/>
+ <menuseparator id="context-sep-open"/>
+ <menuitem id="context-bookmarklink"
+ label="&bookmarkLinkCmd.label;"
+ accesskey="&bookmarkLinkCmd.accesskey;"
+ oncommand="gContextMenu.bookmarkLink();"/>
+ <menuitem id="context-savelink"
+ valueSaveAs="&saveLinkAsCmd.label;"
+ valueSave="&saveLinkCmd.label;"
+ accesskey="&saveLinkCmd.accesskey;"
+ oncommand="gContextMenu.saveLink();"/>
+ <menuitem id="context-copyemail"
+ label="&copyEmailCmd.label;"
+ accesskey="&copyEmailCmd.accesskey;"
+ oncommand="gContextMenu.copyEmail();"/>
+ <menuitem id="context-copylink"
+ label="&copyLinkCmd.label;"
+ accesskey="&copyLinkCmd.accesskey;"
+ command="cmd_copyLink"/>
+ <menuseparator id="context-sep-copylink"/>
+ <menuitem id="context-media-play"
+ label="&mediaPlay.label;"
+ accesskey="&mediaPlay.accesskey;"
+ oncommand="gContextMenu.mediaCommand('play');"/>
+ <menuitem id="context-media-pause"
+ label="&mediaPause.label;"
+ accesskey="&mediaPause.accesskey;"
+ oncommand="gContextMenu.mediaCommand('pause');"/>
+ <menuitem id="context-media-mute"
+ label="&mediaMute.label;"
+ accesskey="&mediaMute.accesskey;"
+ oncommand="gContextMenu.mediaCommand('mute');"/>
+ <menuitem id="context-media-unmute"
+ label="&mediaUnmute.label;"
+ accesskey="&mediaUnmute.accesskey;"
+ oncommand="gContextMenu.mediaCommand('unmute');"/>
+ <menu id="context-media-playbackrate"
+ label="&mediaPlaybackRate.label;"
+ accesskey="&mediaPlaybackRate.accesskey;">
+ <menupopup id="media-playbackrate-popup"
+ oncommand="gContextMenu.mediaCommand('playbackRate',
+ event.target.id.replace(/.*-/, '') / 100);">
+ <menuitem id="context-media-playbackrate-050"
+ label="&mediaPlaybackRate050.label;"
+ accesskey="&mediaPlaybackRate050.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-100"
+ label="&mediaPlaybackRate100.label;"
+ accesskey="&mediaPlaybackRate100.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-125"
+ label="&mediaPlaybackRate125.label;"
+ accesskey="&mediaPlaybackRate125.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-150"
+ label="&mediaPlaybackRate150.label;"
+ accesskey="&mediaPlaybackRate150.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-200"
+ label="&mediaPlaybackRate200.label;"
+ accesskey="&mediaPlaybackRate200.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ </menupopup>
+ </menu>
+ <menuitem id="context-media-loop"
+ label="&mediaLoop.label;"
+ accesskey="&mediaLoop.accesskey;"
+ type="checkbox"
+ oncommand="gContextMenu.mediaCommand('loop');"/>
+ <menuitem id="context-media-showcontrols"
+ label="&mediaShowControls.label;"
+ accesskey="&mediaShowControls.accesskey;"
+ oncommand="gContextMenu.mediaCommand('showcontrols');"/>
+ <menuitem id="context-media-hidecontrols"
+ label="&mediaHideControls.label;"
+ accesskey="&mediaHideControls.accesskey;"
+ oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
+ <menuitem id="context-video-showstats"
+ label="&videoShowStats.label;"
+ accesskey="&videoShowStats.accesskey;"
+ oncommand="gContextMenu.mediaCommand('showstats');"/>
+ <menuitem id="context-video-hidestats"
+ label="&videoHideStats.label;"
+ accesskey="&videoHideStats.accesskey;"
+ oncommand="gContextMenu.mediaCommand('hidestats');"/>
+ <menuitem id="context-video-fullscreen"
+ label="&videoFullScreen.label;"
+ accesskey="&videoFullScreen.accesskey;"
+ oncommand="gContextMenu.fullScreenVideo();"/>
+ <menuseparator id="context-media-sep-commands"/>
+ <menuitem id="context-fitimage"
+ type="checkbox"
+ label="&fitImageCmd.label;"
+ accesskey="&fitImageCmd.accesskey;"
+ oncommand="gContextMenu.toggleImageSize();"/>
+ <menuitem id="context-reloadimage"
+ label="&reloadImageCmd.label;"
+ accesskey="&reloadImageCmd.accesskey;"
+ oncommand="gContextMenu.reloadImage();"/>
+ <menuitem id="context-viewimage"
+ label="&viewImageCmd.label;"
+ accesskey="&viewImageCmd.accesskey;"
+ oncommand="gContextMenu.viewMedia(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-copyimage"
+ label="&copyImageCmd.label;"
+ accesskey="&copyImageCmd.accesskey;"
+ command="cmd_copyImage"/>
+ <menuitem id="context-viewvideo"
+ label="&viewVideoCmd.label;"
+ accesskey="&viewVideoCmd.accesskey;"
+ oncommand="gContextMenu.viewMedia(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-copyvideourl"
+ label="&copyVideoURLCmd.label;"
+ accesskey="&copyVideoURLCmd.accesskey;"
+ oncommand="gContextMenu.copyMediaLocation();"/>
+ <menuitem id="context-copyaudiourl"
+ label="&copyAudioURLCmd.label;"
+ accesskey="&copyAudioURLCmd.accesskey;"
+ oncommand="gContextMenu.copyMediaLocation();"/>
+ <menuseparator id="context-sep-copyimage"/>
+ <menuitem id="context-blockimage"
+ oncommand="gContextMenu.toggleImageBlocking(true);"/>
+ <menuitem id="context-unblockimage"
+ oncommand="gContextMenu.toggleImageBlocking(false);"/>
+ <menuseparator id="context-sep-blockimage"/>
+ <menuitem id="context-saveimage"
+ valueSaveAs="&saveImageAsCmd.label;"
+ valueSave="&saveImageCmd.label;"
+ accesskey="&saveImageCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-setDesktopBackground"
+ label="&setDesktopBackgroundCmd.label;"
+ accesskey="&setDesktopBackgroundCmd.accesskey;"
+ oncommand="gContextMenu.setDesktopBackground();"/>
+ <menuitem id="context-viewimageinfo"
+ label="&viewImageInfoCmd.label;"
+ accesskey="&viewImageInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewImageInfo();"/>
+ <menuitem id="context-savevideo"
+ label="&saveVideoCmd.label;"
+ accesskey="&saveVideoCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-saveaudio"
+ label="&saveAudioCmd.label;"
+ accesskey="&saveAudioCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-video-saveimage"
+ label="&videoSaveImage.label;"
+ accesskey="&videoSaveImage.accesskey;"
+ oncommand="gContextMenu.saveVideoFrameAsImage();"/>
+ <menuseparator id="context-sep-image"/>
+ <menuitem id="context-back"
+ label="&goBackCmd.label;"
+ accesskey="&goBackCmd.accesskey;"
+ oncommand="BrowserBack(event)"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-forward"
+ label="&goForwardCmd.label;"
+ accesskey="&goForwardCmd.accesskey;"
+ oncommand="BrowserForward(event)"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-reload"
+ label="&reloadCmd.label;"
+ accesskey="&reloadCmd.accesskey;"
+ oncommand="BrowserReload(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-stop"
+ label="&stopCmd.label;"
+ accesskey="&stopCmd.accesskey;"
+ disabled="true"
+ oncommand="BrowserStop();"/>
+ <menuseparator id="context-sep-stop"/>
+ <menuitem id="context-bookmarkpage"
+ label="&bookmarkPageCmd.label;"
+ accesskey="&bookmarkPageCmd.accesskey;"
+ oncommand="gContextMenu.bookmarkThisPage();"/>
+ <menuitem id="context-savepage"
+ valueSaveAs="&savePageAsCmd.label;"
+ valueSave="&savePageCmd.label;"
+ accesskey="&savePageCmd.accesskey;"
+ oncommand="saveDocument(window.content.document, true);"/>
+ <menuseparator id="context-sep-viewbgimage"/>
+ <menuitem id="context-viewbgimage"
+ label="&viewBGImageCmd.label;"
+ accesskey="&viewBGImageCmd.accesskey;"
+ oncommand="gContextMenu.viewBGImage(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-undo"/>
+ <menuitem id="context-redo"/>
+ <menuseparator id="context-sep-undo"/>
+ <menuitem id="context-cut"/>
+ <menuitem id="context-copy"/>
+ <menuitem id="context-paste"/>
+ <menuitem id="context-delete"/>
+ <menuseparator id="context-sep-paste"/>
+ <menuitem id="context-selectall"/>
+ <menuseparator id="context-sep-selectall"/>
+ <menuitem id="context-keywordfield"
+ label="&keywordfield.label;"
+ accesskey="&keywordfield.accesskey;"
+ oncommand="AddKeywordForSearchField();"/>
+ <menuitem id="context-searchselect"
+ oncommand="BrowserSearch.loadSearch(gContextMenu.searchSelected(), true, event);"/>
+ <menuseparator id="context-sep-properties"/>
+ <menuitem id="context-viewpartialsource-selection"
+ label="&viewPartialSourceForSelectionCmd.label;"
+ accesskey="&viewPartialSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewPartialSource('selection');"/>
+ <menuitem id="context-viewpartialsource-mathml"
+ label="&viewPartialSourceForMathMLCmd.label;"
+ accesskey="&viewPartialSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewPartialSource('mathml');"/>
+ <menuitem id="context-viewsource"
+ label="&viewPageSourceCmd.label;"
+ accesskey="&viewPageSourceCmd.accesskey;"
+ observes="isImage"
+ oncommand="BrowserViewSource(gContextMenu.browser);"/>
+ <menuitem id="context-viewinfo"
+ label="&viewPageInfoCmd.label;"
+ accesskey="&viewPageInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewInfo();"/>
+ <menuitem id="context-metadata"
+ label="&metadataCmd.label;"
+ accesskey="&metadataCmd.accesskey;"
+ oncommand="gContextMenu.showMetadata();"/>
+ <menuseparator id="frame-sep"/>
+ <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
+ <menupopup id="frame_popup">
+ <menuitem id="context-showonlythisframe"
+ label="&showOnlyThisFrameCmd.label;"
+ accesskey="&showOnlyThisFrameCmd.accesskey;"
+ oncommand="gContextMenu.showOnlyThisFrame();"/>
+ <menuitem id="context-openframeintab"
+ label="&openFrameCmdInTab.label;"
+ accesskey="&openFrameCmdInTab.accesskey;"
+ oncommand="gContextMenu.openFrameInTab(event);"/>
+ <menuitem id="context-openframe"
+ label="&openFrameCmd.label;"
+ accesskey="&openFrameCmd.accesskey;"
+ oncommand="gContextMenu.openFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-reloadframe"
+ label="&reloadFrameCmd.label;"
+ accesskey="&reloadFrameCmd.accesskey;"
+ oncommand="gContextMenu.reloadFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-bookmarkframe"
+ label="&bookmarkFrameCmd.label;"
+ accesskey="&bookmarkFrameCmd.accesskey;"
+ oncommand="gContextMenu.addBookmarkForFrame();"/>
+ <menuitem id="context-saveframe"
+ valueSaveAs="&saveFrameAsCmd.label;"
+ valueSave="&saveFrameCmd.label;"
+ accesskey="&saveFrameCmd.accesskey;"
+ oncommand="saveDocument(gContextMenu.target.ownerDocument, true);"/>
+ <menuseparator/>
+ <menuitem id="context-printframe"
+ label="&printFrameCmd.label;"
+ accesskey="&printFrameCmd.accesskey;"
+ oncommand="gContextMenu.printFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-viewframesource"
+ label="&viewFrameSourceCmd.label;"
+ accesskey="&viewFrameSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewFrameSource();"/>
+ <menuitem id="context-viewframeinfo"
+ label="&viewFrameInfoCmd.label;"
+ accesskey="&viewFrameInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewFrameInfo();"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="spell-separator"/>
+ <menuitem id="spell-check-enabled"
+ label="&spellCheckEnable.label;"
+ type="checkbox"
+ accesskey="&spellCheckEnable.accesskey;"
+ oncommand="InlineSpellCheckerUI.toggleEnabled();"/>
+ <menuitem id="spell-add-dictionaries-main"
+ label="&spellAddDictionaries.label;"
+ accesskey="&spellAddDictionaries.accesskey;"
+ oncommand="openDictionaryList();"/>
+ <menu id="spell-dictionaries"
+ label="&spellDictionaries.label;"
+ accesskey="&spellDictionaries.accesskey;">
+ <menupopup id="spell-dictionaries-menu">
+ <menuseparator id="spell-language-separator"/>
+ <menuitem id="spell-add-dictionaries"
+ label="&spellAddDictionaries.label;"
+ accesskey="&spellAddDictionaries.accesskey;"
+ oncommand="openDictionaryList();"/>
+ </menupopup>
+ </menu>
+ <menuseparator hidden="true" id="context-sep-bidi"/>
+ <menuitem hidden="true" id="context-bidi-text-direction-toggle"
+ label="&bidiSwitchTextDirectionItem.label;"
+ accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+ command="cmd_switchTextDirection"/>
+ <menuitem hidden="true" id="context-bidi-page-direction-toggle"
+ label="&bidiSwitchPageDirectionItem.label;"
+ accesskey="&bidiSwitchPageDirectionItem.accesskey;"
+ oncommand="SwitchDocumentDirection(window.content);"/>
+ <menuseparator id="fill-login-separator" hidden="true"/>
+ <menu id="fill-login"
+ label="&fillLoginMenu.label;"
+ label-login="&fillLoginMenu.label;"
+ label-password="&fillPasswordMenu.label;"
+ label-username="&fillUsernameMenu.label;"
+ accesskey="&fillLoginMenu.accesskey;"
+ accesskey-login="&fillLoginMenu.accesskey;"
+ accesskey-password="&fillPasswordMenu.accesskey;"
+ accesskey-username="&fillUsernameMenu.accesskey;"
+ hidden="true">
+ <menupopup id="fill-login-popup">
+ <menuitem id="fill-login-no-logins"
+ label="&noLoginSuggestions.label;"
+ disabled="true"
+ hidden="true"/>
+ <menuseparator id="saved-logins-separator"/>
+ <menuitem id="fill-login-saved-passwords"
+ label="&viewSavedLogins.label;"
+ oncommand="gContextMenu.openPasswordManager();"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="inspect-separator"
+ hidden="true"/>
+ <menuitem id="context-inspect"
+ hidden="true"
+ label="&devtoolsInspect.label;"
+ accesskey="&devtoolsInspect.accesskey;"
+ oncommand="gContextMenu.inspectNode();"/>
+ </menupopup>
+ </popupset>
+</overlay>
diff --git a/comm/suite/base/content/defaultClientDialog.js b/comm/suite/base/content/defaultClientDialog.js
new file mode 100644
index 0000000000..d44fb4f412
--- /dev/null
+++ b/comm/suite/base/content/defaultClientDialog.js
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+// this dialog can only be opened if we have a shell service
+const {ShellService} = ChromeUtils.import("resource:///modules/ShellService.jsm");
+const nsIPrefBranch = Ci.nsIPrefBranch;
+
+function onLoad()
+{
+ var defaultList = document.getElementById("defaultList");
+ var appTypes = ShellService.shouldBeDefaultClientFor;
+ /* Iterate through the list of possible default client types and check for
+ each list item if we want to be the default for that type using the AND
+ conjunction */
+ for (var i = 0; i < defaultList.getRowCount(); i++) {
+ var currentItem = defaultList.getItemAtIndex(i);
+ try {
+ if (ShellService.isDefaultClient(false, Ci.nsIShellService[currentItem.value])) {
+ currentItem.checked = true;
+ currentItem.disabled = true;
+ }
+ else if (Ci.nsIShellService[currentItem.value] & appTypes)
+ currentItem.checked = true;
+ } catch (e) {
+ currentItem.hidden = true;
+ }
+ }
+}
+
+function onAccept()
+{
+ // for each checked item, if we aren't already the default, make us the default.
+ var appTypes = 0;
+ var appTypesCheck = 0;
+ var defaultList = document.getElementById("defaultList");
+
+ for (var i = 0; i < defaultList.getRowCount(); i++) {
+ var currentItem = defaultList.getItemAtIndex(i);
+ var currentAppType = Ci.nsIShellService[currentItem.value];
+
+ if (currentItem.checked) {
+ appTypesCheck |= currentAppType;
+
+ if (!currentItem.disabled)
+ appTypes |= currentAppType;
+ }
+ }
+
+ if (appTypes)
+ ShellService.setDefaultClient(false, true, appTypes);
+
+ // Update the pref for which app types we should check if we are the default app
+ ShellService.shouldBeDefaultClientFor = appTypesCheck;
+
+ ShellService.shouldCheckDefaultClient = document.getElementById('checkOnStartup').checked;
+}
diff --git a/comm/suite/base/content/defaultClientDialog.xul b/comm/suite/base/content/defaultClientDialog.xul
new file mode 100644
index 0000000000..db075a4b88
--- /dev/null
+++ b/comm/suite/base/content/defaultClientDialog.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<!DOCTYPE window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % defaultClientDTD SYSTEM "chrome://communicator/locale/defaultClientDialog.dtd" >
+ %defaultClientDTD;
+]>
+
+<dialog xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="defaultClientDialog"
+ buttons="accept,cancel"
+ onload="onLoad();"
+ ondialogaccept="return onAccept();"
+ title="&defaultClient.title;">
+
+ <script src="chrome://communicator/content/defaultClientDialog.js"/>
+
+ <description>&defaultClient.intro;</description>
+ <listbox rows="4" id="defaultList">
+ <listitem value="BROWSER" type="checkbox" label="&browser.label;"/>
+ <listitem value="MAIL" type="checkbox" label="&email.label;"/>
+ <listitem value="NEWS" type="checkbox" label="&newsgroups.label;"/>
+ <listitem value="RSS" type="checkbox" label="&feeds.label;"/>
+ </listbox>
+
+ <separator class="thin"/>
+ <checkbox id="checkOnStartup" checked="true" label="&checkOnStartup.label;" accesskey="&checkOnStartup.accesskey;"/>
+
+</dialog>
diff --git a/comm/suite/base/content/extensionsOverlay.css b/comm/suite/base/content/extensionsOverlay.css
new file mode 100644
index 0000000000..9a2b0ce535
--- /dev/null
+++ b/comm/suite/base/content/extensionsOverlay.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+.legacy-warning {
+ display: none;
+}
diff --git a/comm/suite/base/content/findUtils.js b/comm/suite/base/content/findUtils.js
new file mode 100644
index 0000000000..a894e8a4c9
--- /dev/null
+++ b/comm/suite/base/content/findUtils.js
@@ -0,0 +1,141 @@
+/* -*- 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/. */
+
+var gFindBundle;
+
+function nsFindInstData() {}
+nsFindInstData.prototype =
+{
+ // set the next three attributes on your object to override the defaults
+ browser : null,
+
+ get rootSearchWindow() { return this._root || this.window.content; },
+ set rootSearchWindow(val) { this._root = val; },
+
+ get currentSearchWindow() {
+ if (this._current)
+ return this._current;
+
+ var focusedWindow = this.window.document.commandDispatcher.focusedWindow;
+ if (!focusedWindow || focusedWindow == this.window)
+ focusedWindow = this.window.content;
+
+ return focusedWindow;
+ },
+ set currentSearchWindow(val) { this._current = val; },
+
+ get webBrowserFind() { return this.browser.webBrowserFind; },
+
+ init : function() {
+ var findInst = this.webBrowserFind;
+ // set up the find to search the focussedWindow, bounded by the content window.
+ var findInFrames = findInst.QueryInterface(Ci.nsIWebBrowserFindInFrames);
+ findInFrames.rootSearchFrame = this.rootSearchWindow;
+ findInFrames.currentSearchFrame = this.currentSearchWindow;
+
+ // always search in frames for now. We could add a checkbox to the dialog for this.
+ findInst.searchFrames = true;
+ },
+
+ window : window,
+ _root : null,
+ _current : null
+}
+
+// browser is the <browser> element
+// rootSearchWindow is the window to constrain the search to (normally window.content)
+// currentSearchWindow is the frame to start searching (can be, and normally, rootSearchWindow)
+function findInPage(findInstData)
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ findbar.onFindCommand();
+ else if ("findDialog" in window && window.findDialog) // is the find dialog up already?
+ window.findDialog.focus();
+ else
+ {
+ findInstData.init();
+ window.findDialog = window.openDialog("chrome://global/content/finddialog.xul", "_blank", "chrome,resizable=no,dependent=yes", findInstData);
+ }
+}
+
+function findAgainInPage(findInstData, reverse)
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ {
+ // first, look to see whether XPFE typeaheadfind wants to find next
+ var sip = Cc["@mozilla.org/supports-interface-pointer;1"]
+ .createInstance(Ci.nsISupportsInterfacePointer);
+ sip.data = content;
+ Services.obs.notifyObservers(sip, "nsWebBrowserFind_FindAgain", reverse ? "up" : "down");
+ if (sip.data) // XPFE typeahead find was not interested in this find next
+ findbar.onFindAgainCommand(reverse);
+ }
+ else
+ {
+ // get the find service, which stores global find state, and init the
+ // nsIWebBrowser find with it. We don't assume that there was a previous
+ // Find that set this up.
+ var findService = Cc["@mozilla.org/find/find_service;1"]
+ .getService(Ci.nsIFindService);
+
+ var searchString = findService.searchString;
+ if (searchString.length == 0) {
+ // no previous find text
+ findInPage(findInstData);
+ return;
+ }
+
+ findInstData.init();
+ var findInst = findInstData.webBrowserFind;
+ findInst.searchString = searchString;
+ findInst.matchCase = findService.matchCase;
+ findInst.wrapFind = findService.wrapFind;
+ findInst.entireWord = findService.entireWord;
+ findInst.findBackwards = findService.findBackwards ^ reverse;
+
+ var found = findInst.findNext();
+ if (!found) {
+ if (!gFindBundle)
+ gFindBundle = document.getElementById("findBundle");
+
+ Services.prompt.alert(window, gFindBundle.getString("notFoundTitle"), gFindBundle.getString("notFoundWarning"));
+ }
+
+ // Reset to normal value, otherwise setting can get changed in find dialog
+ findInst.findBackwards = findService.findBackwards;
+ }
+}
+
+function canFindAgainInPage()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ // The findbar will just be brought up in an error state if you cannot find text again.
+ return true;
+
+ var findService = Cc["@mozilla.org/find/find_service;1"]
+ .getService(Ci.nsIFindService);
+ return (findService.searchString.length > 0);
+}
+
+function findLinksAsYouType()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("accessibility.typeaheadfind.usefindbar"))
+ findbar.startFastFind(findbar.FIND_LINKS);
+ else
+ goDoCommand("cmd_findTypeLinks");
+}
+
+function findTextAsYouType()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("accessibility.typeaheadfind.usefindbar"))
+ findbar.startFastFind(findbar.FIND_TYPEAHEAD);
+ else
+ goDoCommand("cmd_findTypeText");
+}
diff --git a/comm/suite/base/content/fullscreen-video.xhtml b/comm/suite/base/content/fullscreen-video.xhtml
new file mode 100644
index 0000000000..a56789a06a
--- /dev/null
+++ b/comm/suite/base/content/fullscreen-video.xhtml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 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 html>
+<html xmlns="http://www.w3.org/1999/xhtml" accelerated="11">
+<head>
+ <link href="chrome://communicator/skin/fullscreen-video.css"
+ rel="stylesheet" type="text/css"/>
+ <script>
+ <![CDATA[
+
+var contentVideo = window.arguments[0];
+var isPaused = window.arguments[1];
+var video;
+var closeIcon;
+
+var title = (contentVideo.currentSrc || contentVideo.src).replace(/^.*\//, "");
+try {
+ title = decodeURI(title);
+} catch (e) {}
+document.title = title;
+
+window.addEventListener("focus", onFocus);
+window.addEventListener("unload", onUnload);
+window.addEventListener("keypress", onKeyPress);
+
+function onFocus() {
+ window.removeEventListener("focus", onFocus);
+
+ window.fullScreen = true;
+
+ window.addEventListener("deactivate", autoClose);
+
+ video = document.querySelector("video");
+ closeIcon = document.querySelector("div");
+
+ video.addEventListener("loadeddata", onLoadedData);
+
+ // Automatically close this window when the playback ended, unless the user
+ // interacted with it.
+ video.addEventListener("ended", autoClose);
+ window.addEventListener("click", cancelAutoClose);
+ window.addEventListener("keypress", cancelAutoClose);
+
+ video.addEventListener("playing", hideUI);
+ video.addEventListener("seeked", hideUI);
+ video.addEventListener("seeking", showUI);
+ video.addEventListener("pause", showUI);
+ video.addEventListener("ended", showUI);
+
+ window.addEventListener("mousemove", onMouseMove);
+
+ video.src = contentVideo.currentSrc || contentVideo.src;
+}
+
+function onLoadedData() {
+ video.removeEventListener("loadeddata", onLoadedData);
+ video.volume = contentVideo.volume;
+ video.muted = contentVideo.muted;
+ video.poster = contentVideo.poster;
+
+ if (contentVideo.currentTime && !contentVideo.ended) {
+ video.addEventListener("seeked", playbackStarts);
+
+ video.currentTime = contentVideo.currentTime;
+ } else {
+ playbackStarts();
+ }
+
+ video.controls = true;
+
+ if (!isPaused)
+ video.play();
+}
+
+function onMouseMove() {
+ showUI();
+ resetIdleTimer();
+}
+
+function onUnload() {
+ if (video.currentSrc) {
+ contentVideo.currentTime = video.currentTime;
+ contentVideo.volume = video.volume;
+ contentVideo.muted = video.muted;
+ if (!video.paused && !video.ended) {
+ video.pause();
+ contentVideo.play();
+ }
+ }
+}
+
+function onKeyPress(event) {
+ if (event.keyCode == event.DOM_VK_ESCAPE) {
+ window.close();
+ return;
+ }
+
+ resetIdleTimer();
+
+ if (!video.controls &&
+ String.fromCharCode(event.charCode) == " ")
+ video.pause();
+}
+
+function playbackStarts() {
+ video.removeEventListener("seeked", playbackStarts);
+
+ // Loading the data from the content video may take a second or two. We hide
+ // the video during that period.
+ document.body.style.visibility = "visible";
+ video.focus();
+}
+
+function autoClose() {
+ window.close();
+}
+
+function cancelAutoClose() {
+ video.removeEventListener("ended", autoClose);
+ window.removeEventListener("click", cancelAutoClose);
+ window.removeEventListener("keypress", cancelAutoClose);
+}
+
+var idleTimer = 0;
+function resetIdleTimer() {
+ clearTimeout(idleTimer);
+ idleTimer = setTimeout(hideUI, 2000);
+}
+
+function showUI() {
+ if (!video.controls) {
+ window.setCursor("auto");
+ closeIcon.style.visibility = "visible";
+ video.controls = true;
+ }
+}
+
+function hideUI() {
+ if (!video.paused && !video.ended && !video.seeking && !video.error) {
+ window.setCursor("none");
+ closeIcon.style.visibility = "hidden";
+ video.controls = false;
+ }
+}
+
+ ]]></script>
+</head>
+<body>
+ <div onclick="window.close();"/>
+ <video/>
+</body>
+</html>
diff --git a/comm/suite/base/content/gopherAddon.xhtml b/comm/suite/base/content/gopherAddon.xhtml
new file mode 100644
index 0000000000..002e274972
--- /dev/null
+++ b/comm/suite/base/content/gopherAddon.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % gopherAddonDTD
+ SYSTEM "chrome://communicator/locale/gopherAddon.dtd">
+ %gopherAddonDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&loadError.label;</title>
+ <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css"/>
+ <script>
+ <![CDATA[
+
+ function goToAddOn() {
+ document.location = "https://addons.thunderbird.net/addon/7685";
+ }
+
+ ]]></script>
+ </head>
+
+ <body>
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText">&gopherAddon.title;</h1>
+ </div>
+
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText">&gopherAddon.shortDesc;</p>
+ </div>
+
+ <!-- Long Description -->
+ <div id="errorLongDesc">&gopherAddon.longDesc;</div>
+
+ </div>
+ <!-- Go To Add-On Button -->
+ <button id="errorTryAgain" onclick="goToAddOn()">&goToAddOn.label;</button>
+
+ </div>
+ </body>
+</html>
diff --git a/comm/suite/base/content/helpEditorOverlay.xul b/comm/suite/base/content/helpEditorOverlay.xul
new file mode 100644
index 0000000000..355ea6c59c
--- /dev/null
+++ b/comm/suite/base/content/helpEditorOverlay.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<overlay id="helpEditorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ <![CDATA[
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+
+ function doPublishDialogHelpButton()
+ {
+ var selTab = document.getElementById('TabBox').selectedTab;
+
+ if (selTab.id == "PublishTab")
+ openHelp('comp-doc-publish-publishtab');
+ else
+ openHelp('comp-doc-publish-settingstab');
+ }
+ ]]>
+ </script>
+
+ <dialog id="advancedEditDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('advanced_property_editor');"/>
+
+ <dialog id="imageDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('image_properties');"/>
+
+ <dialog id="linkDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('link_properties');"/>
+
+ <dialog id="publishDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="doPublishDialogHelpButton();"/>
+
+ <dialog id="publishProgressDlg"
+ buttons="cancel,help"
+ ondialoghelp="openHelp('comp-doc-publish-troubleshooting');"/>
+
+ <dialog id="publishSettingsDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('comp-doc-publish-site-settings');"/>
+
+ <dialog id="tableDlg"
+ buttons="accept,extra1,cancel,help"
+ ondialoghelp="openHelp('table_properties');"/>
+
+</overlay>
diff --git a/comm/suite/base/content/helpMessengerOverlay.xul b/comm/suite/base/content/helpMessengerOverlay.xul
new file mode 100644
index 0000000000..1672e80624
--- /dev/null
+++ b/comm/suite/base/content/helpMessengerOverlay.xul
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<overlay id="helpMessengerOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+ </script>
+
+ <dialog id="editDirectories"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('mail-ldap-properties');"/>
+
+ <dialog id="addDirectory"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-ldap-properties');"/>
+
+ <dialog id="accountManager"
+ buttons="accept,cancel,help"
+ ondialoghelp="return doHelpButton();"/>
+
+ <dialog id="FilterEditor"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-filters');"/>
+
+ <dialog id="junkMailInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('mail-junk');"/>
+
+ <dialog id="select-offline"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-offline-items');"/>
+
+ <dialog id="subscribeWindow"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-subscribe');"/>
+
+ <dialog id="mailViewListDialog"
+ buttons="help"
+ ondialoghelp="return openHelp('message-views-using');"/>
+
+ <dialog id="mailViewSetupDialog"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('message-views-create-new');"/>
+
+ <dialog id="msgCompSecurityInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('compose_security');"/>
+
+ <dialog id="msgReadSecurityInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('received_security');"/>
+</overlay>
diff --git a/comm/suite/base/content/helpSecurityOverlay.xul b/comm/suite/base/content/helpSecurityOverlay.xul
new file mode 100644
index 0000000000..2291cdb284
--- /dev/null
+++ b/comm/suite/base/content/helpSecurityOverlay.xul
@@ -0,0 +1,137 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<overlay id="securityManagerOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ <![CDATA[
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+
+ function doCertManagerHelpButton()
+ {
+ var selTab = document.getElementById('certMgrTabbox').selectedItem;
+ var selTabID = selTab.getAttribute('id');
+ switch (selTabID) {
+ case 'mine_tab':
+ openHelp("my_certs");
+ break;
+ case 'others_tab':
+ openHelp("others_certs");
+ break;
+ case 'websites_tab':
+ openHelp("web_certs");
+ break;
+ case 'ca_tab':
+ openHelp("ca_certs");
+ break;
+ case 'orphan_tab':
+ openHelp("orphan_certs");
+ break;
+ }
+ }
+
+ function doDeleteCertificateHelpButton() {
+ let typeFlag = window.arguments[0];
+ switch (typeFlag) {
+ case "mine_tab":
+ openHelp("delete_my_certs");
+ break;
+ case "websites_tab":
+ openHelp("delete_web_certs");
+ break;
+ case "ca_tab":
+ openHelp("delete_ca_certs");
+ break;
+ case "others_tab":
+ openHelp("delete_email_certs");
+ break;
+ }
+ }
+ ]]>
+ </script>
+
+ <dialog id="certmanager"
+ buttons="accept,help"
+ ondialoghelp="return doCertManagerHelpButton();"/>
+
+ <dialog id="certDetails"
+ buttons="accept,help"
+ ondialoghelp="openHelp('cert_details');"/>
+
+ <dialog id="set_password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('change_pwd');"/>
+
+ <dialog id="devicemanager"
+ buttons="accept,help"
+ ondialoghelp="openHelp('sec_devices');"/>
+
+ <dialog id="ssl_warning"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('which_token');"/>
+
+ <dialog id="certAuthAsk"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('which_cert');"/>
+
+ <dialog id="crlImportSuccess"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('validation-crl-import');"/>
+
+ <dialog id="deleteCertificate"
+ buttons="accept,cancel,help"
+ ondialoghelp="doDeleteCertificateHelpButton();"/>
+
+ <dialog id="editCaCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_ca_certs');"/>
+
+ <dialog id="editEmailCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_email_certs');"/>
+
+ <dialog id="editWebsiteCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_web_certs');"/>
+
+ <dialog id="escrowWarnDialog"
+ spacerflex="1"
+ buttons="accept,cancel,help,extra2"
+ ondialoghelp="openHelp('priv_key_copy');"/>
+
+ <dialog id="getp12password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('my_certs');"/>
+
+ <dialog id="setp12password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('cert_backup_pwd');"/>
+
+ <dialog id="crlUpdatePref"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('validation-crl-auto-update-prefs');"/>
+
+ <dialog id="serverCrlNextupdate"
+ buttons="accept,help"
+ ondialoghelp="openHelp('exp_crl');"/>
+
+ <dialog id="crlviewer"
+ buttons="help"
+ ondialoghelp="openHelp('validation-crl-manage');">
+ <hbox id="dialogButtons">
+ <button dlgtype="help"/>
+ </hbox>
+ </dialog>
+
+ <dialog id="reset_password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('reset_pwd');"/>
+
+ <dialog id="download_cert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('new_ca');"/>
+</overlay>
diff --git a/comm/suite/base/content/nsContextMenu.js b/comm/suite/base/content/nsContextMenu.js
new file mode 100644
index 0000000000..26514ff39d
--- /dev/null
+++ b/comm/suite/base/content/nsContextMenu.js
@@ -0,0 +1,1676 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*------------------------------ nsContextMenu ---------------------------------
+| This JavaScript "class" is used to implement the browser's content-area |
+| context menu. |
+| |
+| For usage, see references to this class in navigator.xul. |
+| |
+| Currently, this code is relatively useless for any other purpose. In the |
+| longer term, this code will be restructured to make it more reusable. |
+------------------------------------------------------------------------------*/
+
+var {BrowserUtils} =
+ ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"
+var {LoginManagerContextMenu} =
+ ChromeUtils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", () => {
+ let { InlineSpellChecker } = ChromeUtils.import(
+ "resource://gre/modules/InlineSpellChecker.jsm"
+ );
+ return new InlineSpellChecker();
+});
+
+XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
+ let tmp = {};
+ ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
+ return new tmp.PageMenuParent();
+});
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
+ findCssSelector: "resource://gre/modules/css-selector.js",
+ LoginHelper: "resource://gre/modules/LoginHelper.jsm",
+ LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
+ DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.jsm",
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ ShellService: "resource:///modules/ShellService.jsm",
+
+});
+
+var gContextMenuContentData = null;
+
+function nsContextMenu(aXulMenu, aIsShift, aEvent) {
+ this.shouldDisplay = true;
+ this.initMenu(aXulMenu, aIsShift, aEvent);
+}
+
+// Prototype for nsContextMenu "class."
+nsContextMenu.prototype = {
+ initMenu: function(aXulMenu, aIsShift, aEvent) {
+ // Get contextual info.
+ this.setTarget(document.popupNode, document.popupRangeParent,
+ document.popupRangeOffset);
+
+ if (!this.shouldDisplay)
+ return;
+
+ this.hasPageMenu = false;
+ if (!aIsShift && this.browser.docShell.allowJavascript &&
+ Services.prefs.getBoolPref("javascript.enabled"))
+ this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu);
+
+ this.isTextSelected = this.isTextSelection();
+ this.isContentSelected = this.isContentSelection();
+
+ // Initialize gContextMenuContentData.
+ if (aEvent)
+ this.initContentData(aEvent);
+ // Initialize (disable/remove) menu items.
+ this.initItems();
+ },
+
+ initContentData: function(aEvent) {
+ var addonInfo = {};
+ var subject = {
+ event: aEvent,
+ addonInfo: addonInfo,
+ };
+ subject.wrappedJSObject = subject;
+ // Notifies the Addon-SDK which then populates addonInfo.
+ Services.obs.notifyObservers(subject, "content-contextmenu");
+
+ var popupNode = this.target;
+ var doc = popupNode.ownerDocument;
+
+ var contentType = null;
+ var contentDisposition = null;
+ if (this.onImage) {
+ try {
+ let imageCache = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools)
+ .getImgCacheForDocument(doc);
+ let props = imageCache.findEntryProperties(popupNode.currentURI, doc);
+ if (props) {
+ let nsISupportsCString = Ci.nsISupportsCString;
+ contentType = props.get("type", nsISupportsCString).data;
+ try {
+ contentDisposition = props.get("content-disposition",
+ nsISupportsCString).data;
+ } catch (e) {}
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+
+ gContextMenuContentData = {
+ isRemote: false,
+ event: aEvent,
+ popupNode: popupNode,
+ browser: this.browser,
+ principal: doc.nodePrincipal,
+ addonInfo: addonInfo,
+ documentURIObject: doc.documentURIObject,
+ docLocation: doc.location.href,
+ charSet: doc.characterSet,
+ referrer: doc.referrer,
+ referrerPolicy: doc.referrerPolicy,
+ contentType: contentType,
+ contentDisposition: contentDisposition,
+ frameOuterWindowID: doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID,
+ loginFillInfo: LoginManagerContent.getFieldContext(popupNode),
+ };
+ },
+
+ hiding: function () {
+ gContextMenuContentData = null;
+ InlineSpellCheckerUI.clearSuggestionsFromMenu();
+ InlineSpellCheckerUI.clearDictionaryListFromMenu();
+ InlineSpellCheckerUI.uninit();
+ LoginManagerContextMenu.clearLoginsFromMenu(document);
+ },
+
+ initItems: function() {
+ this.initPageMenuSeparator();
+ this.initOpenItems();
+ this.initNavigationItems();
+ this.initViewItems();
+ this.initMiscItems();
+ this.initSpellingItems();
+ this.initSaveItems();
+ this.initClipboardItems();
+ this.initMetadataItems();
+ this.initMediaPlayerItems();
+ this.initPasswordManagerItems();
+ },
+
+ initPageMenuSeparator: function() {
+ this.showItem("page-menu-separator", this.hasPageMenu);
+ },
+
+ initOpenItems: function() {
+ var showOpen = this.onSaveableLink || (this.inDirList && this.onLink);
+ this.showItem("context-openlinkintab", showOpen);
+ this.showItem("context-openlink", showOpen && !gPrivate);
+ this.showItem("context-openlinkinprivatewindow", showOpen);
+ this.showItem("context-sep-open", showOpen);
+ },
+
+ initNavigationItems: function() {
+ // Back/Forward determined by canGoBack/canGoForward broadcasters.
+ this.setItemAttrFromNode("context-back", "disabled", "canGoBack");
+ this.setItemAttrFromNode("context-forward", "disabled", "canGoForward");
+
+ var showNav = !(this.isContentSelected || this.onLink || this.onImage ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ this.onTextInput);
+
+ this.showItem("context-back", showNav);
+ this.showItem("context-forward", showNav);
+ this.showItem("context-reload", showNav);
+ this.showItem("context-stop", showNav);
+ this.showItem("context-sep-stop", showNav);
+
+ // XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
+ //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
+ },
+
+ initSaveItems: function() {
+ var showSave = !(this.inDirList || this.isContentSelected ||
+ this.onTextInput || this.onStandaloneImage ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ (this.onLink && this.onImage));
+ if (showSave)
+ goSetMenuValue("context-savepage",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-savepage", showSave);
+
+ // Save/send link depends on whether we're in a link.
+ if (this.onSaveableLink)
+ goSetMenuValue("context-savelink",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-savelink", this.onSaveableLink);
+ this.showItem("context-sendlink", this.onSaveableLink);
+
+ // Save image depends on having loaded its content, video and audio don't.
+ showSave = (this.onLoadedImage && this.onCompletedImage) ||
+ this.onStandaloneImage || this.onCanvas;
+ if (showSave)
+ goSetMenuValue("context-saveimage",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-saveimage", showSave);
+ this.showItem("context-savevideo", this.onVideo);
+ this.showItem("context-saveaudio", this.onAudio);
+ this.showItem("context-video-saveimage", this.onVideo);
+ if (this.onVideo)
+ this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
+
+ // Send media URL (but not for canvas, since it's a big data: URL)
+ this.showItem("context-sendimage", showSave && !this.onCanvas);
+ this.showItem("context-sendvideo", this.onVideo);
+ this.showItem("context-sendaudio", this.onAudio);
+ if (this.onVideo)
+ this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL);
+ },
+
+ initViewItems: function() {
+ // View source is always OK, unless in directory listing.
+ this.showItem("context-viewpartialsource-selection",
+ this.isContentSelected && !this.onTextInput);
+ this.showItem("context-viewpartialsource-mathml",
+ this.onMathML && !this.isContentSelected);
+
+ var showView = !(this.inDirList || this.onImage || this.isContentSelected ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ this.onLink || this.onTextInput);
+
+ this.showItem("context-viewsource", showView);
+ this.showItem("context-viewinfo", showView);
+
+ var showInspect = DevToolsShim.isEnabled() &&
+ "gDevTools" in window &&
+ Services.prefs.getBoolPref("devtools.inspector.enabled", false);
+ this.showItem("inspect-separator", showInspect);
+ this.showItem("context-inspect", showInspect);
+
+ this.showItem("context-sep-properties",
+ !(this.inDirList || this.isContentSelected || this.onTextInput ||
+ this.onCanvas || this.onVideo || this.onAudio));
+ // Set Desktop Background depends on whether an image was clicked on,
+ // and requires the shell service.
+ var canSetDesktopBackground = ShellService &&
+ ShellService.canSetDesktopBackground;
+ this.showItem("context-setDesktopBackground",
+ canSetDesktopBackground && (this.onLoadedImage || this.onStandaloneImage));
+
+ this.showItem("context-sep-image",
+ this.onLoadedImage || this.onStandaloneImage);
+
+ if (canSetDesktopBackground && this.onLoadedImage)
+ // Disable the Set Desktop Background menu item if we're still trying to load the image
+ this.setItemAttr("context-setDesktopBackground", "disabled",
+ (("complete" in this.target) && !this.target.complete) ? "true" : null);
+
+ this.showItem("context-fitimage", this.onStandaloneImage &&
+ content.document.imageResizingEnabled);
+ if (this.onStandaloneImage && content.document.imageResizingEnabled) {
+ this.setItemAttr("context-fitimage", "disabled",
+ content.document.imageIsOverflowing ? null : "true");
+ this.setItemAttr("context-fitimage", "checked",
+ content.document.imageIsResized ? "true" : null);
+ }
+
+ // Reload image depends on an image that's not fully loaded
+ this.showItem("context-reloadimage", (this.onImage && !this.onCompletedImage));
+
+ // View image depends on having an image that's not standalone
+ // (or is in a frame), or a canvas.
+ this.showItem("context-viewimage",
+ (this.onImage && (!this.inSyntheticDoc || this.inFrame)) ||
+ this.onCanvas);
+
+ // View video depends on not having a standalone video.
+ this.showItem("context-viewvideo", this.onVideo &&
+ (!this.inSyntheticDoc || this.inFrame));
+ this.setItemAttr("context-viewvideo", "disabled", !this.mediaURL);
+
+ // View background image depends on whether there is one, but don't make
+ // background images of a stand-alone media document available
+ this.showItem("context-viewbgimage", showView && !this.inSyntheticDoc);
+ this.showItem("context-sep-viewbgimage", showView && !this.inSyntheticDoc);
+ this.setItemAttr("context-viewbgimage", "disabled", this.hasBGImage ? null : "true");
+
+ this.showItem("context-viewimageinfo", this.onImage);
+
+ // Hide Block and Unblock menuitems.
+ this.showItem("context-blockimage", false);
+ this.showItem("context-unblockimage", false);
+ this.showItem("context-sep-blockimage", false);
+
+ // Block image depends on whether an image was clicked on.
+ if (this.onImage) {
+ var uri = Services.io.newURI(this.mediaURL);
+ if (uri instanceof Ci.nsIURL && uri.host) {
+ var serverLabel = uri.host;
+ // Limit length to max 15 characters.
+ serverLabel = serverLabel.replace(/^www\./i, "");
+ if (serverLabel.length > 15)
+ serverLabel = serverLabel.substr(0, 15) + this.ellipsis;
+
+ // Set label and accesskey for appropriate action and unhide menuitem.
+ var id = "context-blockimage";
+ var attr = "blockImage";
+ if (Services.perms.testPermission(uri, "image") == Services.perms.DENY_ACTION) {
+ id = "context-unblockimage";
+ attr = "unblockImage";
+ }
+ const bundle = document.getElementById("contentAreaCommandsBundle");
+ this.setItemAttr(id, "label",
+ bundle.getFormattedString(attr, [serverLabel]));
+ this.setItemAttr(id, "accesskey",
+ bundle.getString(attr + ".accesskey"));
+ this.showItem(id, true);
+ this.showItem("context-sep-blockimage", true);
+ }
+ }
+ },
+
+ initMiscItems: function() {
+ // Use "Bookmark This Link" if on a link.
+ this.showItem("context-bookmarkpage",
+ !(this.isContentSelected || this.onTextInput ||
+ this.onStandaloneImage || this.onVideo || this.onAudio));
+ this.showItem("context-bookmarklink", this.onLink && !this.onMailtoLink);
+ this.showItem("context-searchselect", this.isTextSelected);
+ this.showItem("context-keywordfield", this.onTextInput && this.onKeywordField);
+ this.showItem("frame", this.inFrame);
+ this.showItem("frame-sep", this.inFrame);
+ if (this.inFrame)
+ goSetMenuValue("context-saveframe",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+
+ // BiDi UI
+ this.showItem("context-sep-bidi", !this.onNumeric && gShowBiDi);
+ this.showItem("context-bidi-text-direction-toggle",
+ this.onTextInput && !this.onNumeric && gShowBiDi);
+ this.showItem("context-bidi-page-direction-toggle",
+ !this.onTextInput && gShowBiDi);
+ },
+
+ initSpellingItems: function() {
+ var canSpell = InlineSpellCheckerUI.canSpellCheck &&
+ !InlineSpellCheckerUI.initialSpellCheckPending &&
+ this.canSpellCheck;
+ let showDictionaries = canSpell && InlineSpellCheckerUI.enabled;
+ var onMisspelling = InlineSpellCheckerUI.overMisspelling;
+ var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
+ this.showItem("spell-check-enabled", canSpell);
+ this.showItem("spell-separator", canSpell);
+ if (canSpell)
+ this.setItemAttr("spell-check-enabled", "checked", InlineSpellCheckerUI.enabled);
+ this.showItem("spell-add-to-dictionary", onMisspelling);
+ this.showItem("spell-undo-add-to-dictionary", showUndo);
+ this.showItem("spell-ignore-word", onMisspelling);
+
+ // suggestion list
+ this.showItem("spell-add-separator", onMisspelling);
+ this.showItem("spell-suggestions-separator", onMisspelling || showUndo);
+ if (onMisspelling) {
+ var suggestionsSeparator = document.getElementById("spell-add-separator");
+ var numsug = InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode, suggestionsSeparator, 5);
+ this.showItem("spell-no-suggestions", numsug == 0);
+ } else {
+ this.showItem("spell-no-suggestions", false);
+ }
+
+ // dictionary list
+ this.showItem("spell-dictionaries", showDictionaries);
+ var dictMenu = document.getElementById("spell-dictionaries-menu");
+ if (canSpell && dictMenu) {
+ var dictSep = document.getElementById("spell-language-separator");
+ let count = InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
+ this.showItem(dictSep, count > 0);
+ this.showItem("spell-add-dictionaries-main", false);
+ }
+ else if (this.onEditableArea) {
+ // when there is no spellchecker but we might be able to spellcheck
+ // add the add to dictionaries item. This will ensure that people
+ // with no dictionaries will be able to download them
+ this.showItem("spell-language-separator", showDictionaries);
+ this.showItem("spell-add-dictionaries-main", showDictionaries);
+ }
+ else
+ this.showItem("spell-add-dictionaries-main", false);
+ },
+
+ initClipboardItems: function() {
+ // Copy depends on whether there is selected text.
+ // Enabling this context menu item is now done through the global
+ // command updating system
+ // this.setItemAttr("context-copy", "disabled", !this.isTextSelected());
+
+ goUpdateGlobalEditMenuItems();
+
+ this.showItem("context-undo", this.onTextInput);
+ this.showItem("context-redo", this.onTextInput);
+ this.showItem("context-sep-undo", this.onTextInput);
+ this.showItem("context-cut", this.onTextInput);
+ this.showItem("context-copy", this.isContentSelected || this.onTextInput);
+ this.showItem("context-paste", this.onTextInput);
+ this.showItem("context-delete", this.onTextInput);
+ this.showItem("context-sep-paste", this.onTextInput);
+ this.showItem("context-selectall", !(this.onLink || this.onImage ||
+ this.onVideo || this.onAudio ||
+ this.inSyntheticDoc));
+ this.showItem("context-sep-selectall",
+ this.isContentSelected && !this.onTextInput);
+ // In a text area there will be nothing after select all, so we don't want a sep
+ // Otherwise, if there's text selected then there are extra menu items
+ // (search for selection and view selection source), so we do want a sep
+
+ // XXX dr
+ // ------
+ // nsDocumentViewer.cpp has code to determine whether we're
+ // on a link or an image. we really ought to be using that...
+
+ // Copy email link depends on whether we're on an email link.
+ this.showItem("context-copyemail", this.onMailtoLink);
+
+ // Copy link location depends on whether we're on a link.
+ this.showItem("context-copylink", this.onLink);
+ this.showItem("context-sep-copylink", this.onLink);
+
+ // Copy image location depends on whether we're on an image.
+ this.showItem("context-copyimage", this.onImage);
+ this.showItem("context-copyvideourl", this.onVideo);
+ this.showItem("context-copyaudiourl", this.onAudio);
+ if (this.onVideo)
+ this.setItemAttr("context-copyvideourl", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-copyaudiourl", "disabled", !this.mediaURL);
+ this.showItem("context-sep-copyimage",
+ this.onImage || this.onVideo || this.onAudio);
+ },
+
+ initMetadataItems: function() {
+ // Show if user clicked on something which has metadata.
+ this.showItem("context-metadata", this.onMetaDataItem);
+ },
+
+ initMediaPlayerItems: function() {
+ var onMedia = (this.onVideo || this.onAudio);
+ // Several mutually exclusive items... play/pause, mute/unmute, show/hide
+ this.showItem("context-media-play",
+ onMedia && (this.target.paused || this.target.ended));
+ this.showItem("context-media-pause",
+ onMedia && !this.target.paused && !this.target.ended);
+ this.showItem("context-media-mute", onMedia && !this.target.muted);
+ this.showItem("context-media-unmute", onMedia && this.target.muted);
+ this.showItem("context-media-playbackrate",
+ onMedia && this.target.duration != Number.POSITIVE_INFINITY);
+ this.showItem("context-media-loop", onMedia);
+ this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
+ this.showItem("context-media-hidecontrols", onMedia && this.target.controls);
+ this.showItem("context-video-fullscreen", this.onVideo &&
+ !this.target.ownerDocument.fullscreenElement);
+
+ var statsShowing = this.onVideo && this.target.mozMediaStatisticsShowing;
+ this.showItem("context-video-showstats",
+ this.onVideo && this.target.controls && !statsShowing);
+ this.showItem("context-video-hidestats",
+ this.onVideo && this.target.controls && statsShowing);
+
+ // Disable them when there isn't a valid media source loaded.
+ if (onMedia) {
+ this.setItemAttr("context-media-playbackrate-050", "checked", this.target.playbackRate == 0.5);
+ this.setItemAttr("context-media-playbackrate-100", "checked", this.target.playbackRate == 1.0);
+ this.setItemAttr("context-media-playbackrate-125", "checked", this.target.playbackRate == 1.25);
+ this.setItemAttr("context-media-playbackrate-150", "checked", this.target.playbackRate == 1.5);
+ this.setItemAttr("context-media-playbackrate-200", "checked", this.target.playbackRate == 2.0);
+ this.setItemAttr("context-media-loop", "checked", this.target.loop);
+ var hasError = this.target.error != null ||
+ this.target.networkState == this.target.NETWORK_NO_SOURCE;
+ this.setItemAttr("context-media-play", "disabled", hasError);
+ this.setItemAttr("context-media-pause", "disabled", hasError);
+ this.setItemAttr("context-media-mute", "disabled", hasError);
+ this.setItemAttr("context-media-unmute", "disabled", hasError);
+ this.setItemAttr("context-media-playbackrate", "disabled", hasError);
+ this.setItemAttr("context-media-showcontrols", "disabled", hasError);
+ this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
+ if (this.onVideo) {
+ let canSave = this.target.readyState >= this.target.HAVE_CURRENT_DATA;
+ this.setItemAttr("context-video-saveimage", "disabled", !canSave);
+ this.setItemAttr("context-video-fullscreen", "disabled", hasError);
+ this.setItemAttr("context-video-showstats", "disabled", hasError);
+ this.setItemAttr("context-video-hidestats", "disabled", hasError);
+ }
+ }
+ this.showItem("context-media-sep-commands", onMedia);
+ },
+
+ initPasswordManagerItems: function() {
+ let fillMenu = document.getElementById("fill-login");
+ // If no fill Menu, probably mailContext so nothing to set up.
+ if (!fillMenu)
+ return;
+
+ let loginFillInfo = gContextMenuContentData && gContextMenuContentData.loginFillInfo;
+
+ // If we could not find a password field we
+ // don't want to show the form fill option.
+ let showFill = loginFillInfo && loginFillInfo.passwordField.found;
+
+ // Disable the fill option if the user has set a master password
+ // or if the password field or target field are disabled.
+ let disableFill = !loginFillInfo ||
+ !Services.logins ||
+ !Services.logins.isLoggedIn ||
+ loginFillInfo.passwordField.disabled ||
+ (!this.onPassword && loginFillInfo.usernameField.disabled);
+
+ this.showItem("fill-login-separator", showFill);
+ this.showItem("fill-login", showFill);
+ this.setItemAttr("fill-login", "disabled", disableFill);
+
+ // Set the correct label for the fill menu
+ if (this.onPassword) {
+ fillMenu.setAttribute("label", fillMenu.getAttribute("label-password"));
+ fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-password"));
+ } else {
+ fillMenu.setAttribute("label", fillMenu.getAttribute("label-login"));
+ fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-login"));
+ }
+
+ if (!showFill || disableFill) {
+ return;
+ }
+ let documentURI = gContextMenuContentData.documentURIObject;
+ let fragment = LoginManagerContextMenu.addLoginsToMenu(this.target, this.browser, documentURI);
+
+ this.showItem("fill-login-no-logins", !fragment);
+
+ if (!fragment) {
+ return;
+ }
+ let popup = document.getElementById("fill-login-popup");
+ let insertBeforeElement = document.getElementById("fill-login-no-logins");
+ popup.insertBefore(fragment, insertBeforeElement);
+ },
+
+ openPasswordManager: function() {
+ // LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
+ toDataManager(gContextMenuContentData.documentURIObject.host + '|passwords');
+ },
+
+ /**
+ * Retrieve the array of CSS selectors corresponding to the provided node. The first item
+ * of the array is the selector of the node in its owner document. Additional items are
+ * used if the node is inside a frame, each representing the CSS selector for finding the
+ * frame element in its parent document.
+ *
+ * This format is expected by DevTools in order to handle the Inspect Node context menu
+ * item.
+ *
+ * @param {Node}
+ * The node for which the CSS selectors should be computed
+ * @return {Array} array of css selectors (strings).
+ */
+ getNodeSelectors: function(node) {
+ let selectors = [];
+ while (node) {
+ selectors.push(findCssSelector(node));
+ node = node.ownerGlobal.frameElement;
+ }
+
+ return selectors;
+ },
+
+ inspectNode: function() {
+ let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
+ return DevToolsShim.inspectNode(gBrowser.selectedTab,
+ this.getNodeSelectors(this.target));
+ },
+
+ // Set various context menu attributes based on the state of the world.
+ setTarget: function(aNode, aRangeParent, aRangeOffset) {
+ // Currently "isRemote" is always false.
+ //this.isRemote = gContextMenuContentData && gContextMenuContentData.isRemote;
+
+ const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ // Initialize contextual info.
+ this.onImage = false;
+ this.onLoadedImage = false;
+ this.onCompletedImage = false;
+ this.onStandaloneImage = false;
+ this.onCanvas = false;
+ this.onVideo = false;
+ this.onAudio = false;
+ this.onMetaDataItem = false;
+ this.onTextInput = false;
+ this.onNumeric = false;
+ this.onKeywordField = false;
+ this.mediaURL = "";
+ this.onLink = false;
+ this.onMailtoLink = false;
+ this.onSaveableLink = false;
+ this.inDirList = false;
+ this.link = null;
+ this.linkURL = "";
+ this.linkURI = null;
+ this.linkProtocol = "";
+ this.linkHasNoReferrer = false;
+ this.onMathML = false;
+ this.inFrame = false;
+ this.inSyntheticDoc = false;
+ this.hasBGImage = false;
+ this.bgImageURL = "";
+ this.autoDownload = false;
+ this.isTextSelected = false;
+ this.isContentSelected = false;
+ this.onEditableArea = false;
+ this.canSpellCheck = false;
+ this.onPassword = false;
+
+ // Remember the node that was clicked.
+ this.target = aNode;
+
+ if (aNode.nodeType == Node.DOCUMENT_NODE ||
+ // Not display on XUL element but relax for <label class="text-link">
+ (aNode.namespaceURI == xulNS && !isXULTextLinkLabel(aNode))) {
+ this.shouldDisplay = false;
+ return;
+ }
+
+ let editFlags = SpellCheckHelper.isEditable(this.target, window);
+ this.browser = this.target.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ this.principal = this.target.ownerDocument.nodePrincipal;
+
+ this.autoDownload = Services.prefs.getBoolPref("browser.download.useDownloadDir");
+
+ // Check if we are in a synthetic document (stand alone image, video, etc.).
+ this.inSyntheticDoc = this.target.ownerDocument.mozSyntheticDocument;
+ // First, do checks for nodes that never have children.
+ if (this.target.nodeType == Node.ELEMENT_NODE) {
+ // See if the user clicked on an image.
+ if (this.target instanceof Ci.nsIImageLoadingContent &&
+ this.target.currentURI) {
+ this.onImage = true;
+
+ var request =
+ this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
+ this.onLoadedImage = true;
+ if (request &&
+ (request.imageStatus & request.STATUS_LOAD_COMPLETE) &&
+ !(request.imageStatus & request.STATUS_ERROR)) {
+ this.onCompletedImage = true;
+ }
+
+ this.mediaURL = this.target.currentURI.spec;
+
+ if (this.target.ownerDocument instanceof ImageDocument)
+ this.onStandaloneImage = true;
+ }
+ else if (this.target instanceof HTMLCanvasElement) {
+ this.onCanvas = true;
+ }
+ else if (this.target instanceof HTMLVideoElement) {
+ // Gecko always creates a HTMLVideoElement when loading an ogg file
+ // directly. If the media is actually audio, be smarter and provide
+ // a context menu with audio operations.
+ if (this.target.readyState >= this.target.HAVE_METADATA &&
+ (this.target.videoWidth == 0 || this.target.videoHeight == 0))
+ this.onAudio = true;
+ else
+ this.onVideo = true;
+
+ this.mediaURL = this.target.currentSrc || this.target.src;
+ }
+ else if (this.target instanceof HTMLAudioElement) {
+ this.onAudio = true;
+ this.mediaURL = this.target.currentSrc || this.target.src;
+ }
+ else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
+ this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
+ this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
+ this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
+ this.onPassword = (editFlags & SpellCheckHelper.PASSWORD) !== 0;
+ if (this.onEditableArea) {
+ InlineSpellCheckerUI.init(this.target.editor);
+ InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
+ }
+ this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
+ }
+ else if ( this.target instanceof HTMLHtmlElement ) {
+ // pages with multiple <body>s are lame. we'll teach them a lesson.
+ var bodyElt = this.target.ownerDocument.body;
+ if (bodyElt) {
+ var computedURL = this.getComputedURL(bodyElt, "background-image");
+ if (computedURL) {
+ this.hasBGImage = true;
+ this.bgImageURL = makeURLAbsolute(bodyElt.baseURI, computedURL);
+ }
+ }
+ }
+ else if ("HTTPIndex" in content &&
+ content.HTTPIndex instanceof Ci.nsIHTTPIndex) {
+ this.inDirList = true;
+ // Bubble outward till we get to an element with URL attribute
+ // (which should be the href).
+ var root = this.target;
+ while (root && !this.link) {
+ if (root.tagName == "tree") {
+ // Hit root of tree; must have clicked in empty space;
+ // thus, no link.
+ break;
+ }
+
+ if (root.getAttribute("URL")) {
+ // Build pseudo link object so link-related functions work.
+ this.onLink = true;
+ this.link = {href: root.getAttribute("URL"),
+ getAttribute: function(attr) {
+ if (attr == "title") {
+ return root.firstChild.firstChild.getAttribute("label");
+ } else {
+ return "";
+ }
+ }
+ };
+ this.linkURL = this.getLinkURL();
+ this.linkURI = this.getLinkURI();
+ this.linkProtocol = this.getLinkProtocol();
+ this.onMailtoLink = (this.linkProtocol == "mailto");
+
+ // If element is a directory, then you can't save it.
+ this.onSaveableLink = root.getAttribute("container") != "true";
+ }
+ else {
+ root = root.parentNode;
+ }
+ }
+ }
+
+ this.canSpellCheck = this._isSpellCheckEnabled(this.target);
+ }
+ else if (this.target.nodeType == Node.TEXT_NODE) {
+ // For text nodes, look at the parent node to determine the spellcheck attribute.
+ this.canSpellCheck = this.target.parentNode &&
+ this._isSpellCheckEnabled(this.target);
+ }
+
+ // We have meta data on images.
+ this.onMetaDataItem = this.onImage;
+
+ // Bubble out, looking for items of interest
+ const NS_MathML = "http://www.w3.org/1998/Math/MathML";
+ const XMLNS = "http://www.w3.org/XML/1998/namespace";
+ var elem = this.target;
+ while (elem) {
+ if (elem.nodeType == Node.ELEMENT_NODE) {
+ // Link?
+ if (!this.onLink &&
+ (isXULTextLinkLabel(elem) ||
+ (elem instanceof HTMLAnchorElement && elem.href) ||
+ (elem instanceof HTMLAreaElement && elem.href) ||
+ elem instanceof HTMLLinkElement ||
+ (elem.namespaceURI == NS_MathML && elem.hasAttribute("href")) ||
+ elem.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) {
+ // Clicked on a link.
+ this.onLink = true;
+ this.onMetaDataItem = true;
+ // Remember corresponding element.
+ this.link = elem;
+ this.linkURL = this.getLinkURL();
+ this.linkURI = this.getLinkURI();
+ this.linkProtocol = this.getLinkProtocol();
+ this.onMailtoLink = (this.linkProtocol == "mailto");
+ // Remember if it is saveable.
+ this.onSaveableLink = this.isLinkSaveable();
+ this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
+ }
+
+ // Text input?
+ if (!this.onTextInput) {
+ // Clicked on a link.
+ this.onTextInput = this.isTargetATextBox(elem);
+ }
+
+ // Metadata item?
+ if (!this.onMetaDataItem) {
+ // We currently display metadata on anything which fits
+ // the below test.
+ if ((elem instanceof HTMLQuoteElement && elem.cite) ||
+ (elem instanceof HTMLTableElement && elem.summary) ||
+ (elem instanceof HTMLModElement && (elem.cite || elem.dateTime)) ||
+ (elem instanceof HTMLElement && (elem.title || elem.lang)) ||
+ elem.getAttributeNS(XMLNS, "lang")) {
+ dump("On metadata item.\n");
+ this.onMetaDataItem = true;
+ }
+ }
+
+ // Background image? Don't bother if we've already found a
+ // background image further down the hierarchy. Otherwise,
+ // we look for the computed background-image style.
+ if (!this.hasBGImage) {
+ var bgImgUrl = this.getComputedURL(elem, "background-image");
+ if (bgImgUrl) {
+ this.hasBGImage = true;
+ this.bgImageURL = makeURLAbsolute(elem.baseURI, bgImgUrl);
+ }
+ }
+ }
+ elem = elem.parentNode;
+ }
+
+ // See if the user clicked on MathML
+ if ((this.target.nodeType == Node.TEXT_NODE &&
+ this.target.parentNode.namespaceURI == NS_MathML) ||
+ (this.target.namespaceURI == NS_MathML))
+ this.onMathML = true;
+
+ // See if the user clicked in a frame.
+ var docDefaultView = this.target.ownerDocument.defaultView;
+ if (docDefaultView != docDefaultView.top)
+ this.inFrame = true;
+
+ // if the document is editable, show context menu like in text inputs
+ if (!this.onEditableArea) {
+ if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
+ // If this.onEditableArea is false but editFlags is CONTENTEDITABLE,
+ // then the document itself must be editable.
+ this.onTextInput = true;
+ this.onKeywordField = false;
+ this.onImage = false;
+ this.onLoadedImage = false;
+ this.onCompletedImage = false;
+ this.onMathML = false;
+ this.inFrame = false;
+ this.hasBGImage = false;
+ this.onEditableArea = true;
+ var win = this.target.ownerDocument.defaultView;
+ var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIEditingSession);
+ InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
+ InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
+ var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
+ this.showItem("spell-check-enabled", canSpell);
+ this.showItem("spell-separator", canSpell);
+ }
+ }
+
+ function isXULTextLinkLabel(node) {
+ return node.namespaceURI == xulNS &&
+ node.tagName == "label" &&
+ node.classList.contains('text-link') &&
+ node.href;
+ }
+ },
+
+ _isSpellCheckEnabled: function(aNode) {
+ // We can always force-enable spellchecking on textboxes
+ if (this.isTargetATextBox(aNode)) {
+ return true;
+ }
+ // We can never spell check something which is not content editable
+ var editable = aNode.isContentEditable;
+ if (!editable && aNode.ownerDocument) {
+ editable = aNode.ownerDocument.designMode == "on";
+ }
+ if (!editable) {
+ return false;
+ }
+ // Otherwise make sure that nothing in the parent chain disables spellchecking
+ return aNode.spellcheck;
+ },
+
+ // Returns the computed style attribute for the given element.
+ getComputedStyle: function(aElem, aProp) {
+ return aElem.ownerDocument
+ .defaultView
+ .getComputedStyle(aElem, "").getPropertyValue(aProp);
+ },
+
+ // Returns a "url"-type computed style attribute value, with the url() stripped.
+ getComputedURL: function(aElem, aProp) {
+ var url = aElem.ownerDocument.defaultView
+ .getComputedStyle(aElem, "")
+ .getPropertyCSSValue(aProp);
+ if (url instanceof CSSPrimitiveValue)
+ url = [url];
+
+ for (var i = 0; i < url.length; i++)
+ if (url[i].primitiveType == CSSPrimitiveValue.CSS_URI)
+ return url[i].getStringValue();
+ return null;
+ },
+
+ // Returns true if clicked-on link targets a resource that can be saved.
+ isLinkSaveable: function() {
+ return this.linkProtocol && this.linkProtocol != "mailto" &&
+ this.linkProtocol != "javascript";
+ },
+
+ // Block/Unblock image from loading in the future.
+ toggleImageBlocking: function(aBlock) {
+ const uri = Services.io.newURI(this.mediaURL);
+ if (aBlock)
+ Services.perms.add(uri, "image", Services.perms.DENY_ACTION);
+ else
+ Services.perms.remove(uri, "image");
+ },
+
+ _openLinkInParameters : function (extra) {
+ let params = { charset: gContextMenuContentData.charSet,
+ originPrincipal: this.principal,
+ triggeringPrincipal: this.principal,
+ referrerURI: gContextMenuContentData.documentURIObject,
+ referrerPolicy: gContextMenuContentData.referrerPolicy,
+ noReferrer: this.linkHasNoReferrer || this.onPlainTextLink };
+ for (let p in extra) {
+ params[p] = extra[p];
+ }
+
+ // If we want to change userContextId, we must be sure that we don't
+ // propagate the referrer.
+ if ("userContextId" in params &&
+ params.userContextId != this.principal.originAttributes.userContextId) {
+ params.noReferrer = true;
+ }
+
+ return params;
+ },
+
+ // Open linked-to URL in a new tab.
+ openLinkInTab: function(aEvent) {
+ urlSecurityCheck(this.linkURL, this.principal);
+ let referrerURI = gContextMenuContentData.documentURIObject;
+
+ // if the mixedContentChannel is present and the referring URI passes
+ // a same origin check with the target URI, we can preserve the users
+ // decision of disabling MCB on a page for it's child tabs.
+ let persistAllowMixedContentInChildTab = false;
+
+ if (this.browser.docShell.mixedContentChannel) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let targetURI = this.linkURI;
+ sm.checkSameOriginURI(referrerURI, targetURI, false);
+ persistAllowMixedContentInChildTab = true;
+ }
+ catch (e) { }
+ }
+
+ let params = {
+ allowMixedContent: persistAllowMixedContentInChildTab,
+ userContextId: parseInt(aEvent.target.getAttribute('usercontextid')),
+ };
+
+ openLinkIn(this.linkURL,
+ aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
+ this._openLinkInParameters(params));
+ },
+
+ // Open linked-to URL in a new window.
+ openLinkInWindow: function() {
+ urlSecurityCheck(this.linkURL, this.principal);
+ openLinkIn(this.linkURL, "window", this._openLinkInParameters());
+ },
+
+ // Open linked-to URL in a private window.
+ openLinkInPrivateWindow: function() {
+ urlSecurityCheck(this.linkURL, this.principal);
+ openLinkIn(this.linkURL, "window",
+ this._openLinkInParameters({ private: true }));
+ },
+
+ // Open frame in a new tab.
+ openFrameInTab: function(aEvent) {
+ let referrer = gContextMenuContentData.referrer;
+ openLinkIn(gContextMenuContentData.docLocation,
+ aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
+ { charset: gContextMenuContentData.charSet,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ // Reload clicked-in frame.
+ reloadFrame: function() {
+ this.target.ownerDocument.location.reload();
+ },
+
+ // Open clicked-in frame in its own window.
+ openFrame: function() {
+ let referrer = gContextMenuContentData.referrer;
+ openLinkIn(gContextMenuContentData.docLocation, "window",
+ { charset: gContextMenuContentData.charSet,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ printFrame: function() {
+ PrintUtils.printWindow(gContextMenuContentData.frameOuterWindowID,
+ this.browser);
+ },
+
+ // Open clicked-in frame in the same window
+ showOnlyThisFrame: function() {
+ urlSecurityCheck(gContextMenuContentData.docLocation,
+ this.principal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ let referrer = gContextMenuContentData.referrer;
+ openUILinkIn(gContextMenuContentData.docLocation, "current",
+ { disallowInheritPrincipal: true,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ // View Partial Source
+ viewPartialSource: function(aContext) {
+ var browser = getBrowser().selectedBrowser;
+ var target = aContext == "mathml" ? this.target : null;
+ gViewSourceUtils.viewPartialSourceInBrowser(browser, target, null);
+ },
+
+ // Open new "view source" window with the frame's URL.
+ viewFrameSource: function() {
+ gViewSourceUtils.viewSource({
+ browser: this.browser,
+ URL: gContextMenuContentData.docLocation,
+ outerWindowID: gContextMenuContentData.frameOuterWindowID,
+ });
+ },
+
+ viewInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, null,
+ null, null, this.browser);
+ },
+
+ viewImageInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, "mediaTab",
+ this.target, null, this.browser);
+ },
+
+ viewFrameInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, null, null,
+ gContextMenuContentData.frameOuterWindowID, this.browser);
+ },
+
+ toggleImageSize: function() {
+ content.document.toggleImageSize();
+ },
+
+ // Reload image
+ reloadImage: function() {
+ urlSecurityCheck(this.mediaURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ if (this.target instanceof Ci.nsIImageLoadingContent)
+ this.target.forceReload();
+ },
+
+ // Change current window to the URL of the image, video, or audio.
+ viewMedia(e) {
+ let doc = this.target.ownerDocument;
+ let where = whereToOpenLink(e);
+
+ if (this.onCanvas) {
+ let systemPrincipal = Services.scriptSecurityManager
+ .getSystemPrincipal();
+ this.target.toBlob((blob) => {
+ openUILinkIn(URL.createObjectURL(blob), where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: systemPrincipal,
+ });
+ });
+ } else {
+ urlSecurityCheck(this.mediaURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ openUILinkIn(this.mediaURL, where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: this.target.nodePrincipal,
+ });
+ }
+ },
+
+ saveVideoFrameAsImage: function () {
+ urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ var name = "snapshot.jpg";
+ try {
+ let uri = makeURI(this.mediaURL);
+ let url = uri.QueryInterface(Ci.nsIURL);
+ if (url.fileBaseName)
+ name = decodeURI(url.fileBaseName) + ".jpg";
+ } catch (e) { }
+ var video = this.target;
+ var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+ var ctxDraw = canvas.getContext("2d");
+ ctxDraw.drawImage(video, 0, 0);
+ saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle",
+ true, true,
+ this.target.ownerDocument.documentURIObject,
+ null, null, null, (gPrivate ? true : false),
+ this.principal);
+ },
+
+ // Full screen video playback
+ fullScreenVideo: function() {
+ var isPaused = this.target.paused && this.target.currentTime > 0;
+ this.target.pause();
+
+ openDialog("chrome://communicator/content/fullscreen-video.xhtml",
+ "", "chrome,centerscreen,dialog=no", this.target, isPaused);
+ },
+
+ // Change current window to the URL of the background image.
+ viewBGImage(e) {
+ urlSecurityCheck(this.bgImageURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+
+ let doc = this.target.ownerDocument;
+ let where = whereToOpenLink(e);
+ openUILinkIn(this.bgImageURL, where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: this.target.nodePrincipal,
+ });
+ },
+
+ setDesktopBackground: function() {
+ let url = (new URL(this.target.ownerDocument.location.href)).pathname;
+ let imageName = url.substr(url.lastIndexOf("/") + 1);
+ openDialog("chrome://communicator/content/setDesktopBackground.xul",
+ "_blank", "chrome,modal,titlebar,centerscreen", this.target,
+ imageName);
+ },
+
+ // Save URL of clicked-on frame.
+ saveFrame: function() {
+ saveDocument(this.target.ownerDocument, true);
+ },
+
+ // Save URL of clicked-on link.
+ saveLink: function() {
+ var doc = this.target.ownerDocument;
+ urlSecurityCheck(this.linkURL, this.principal);
+ this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
+ },
+
+ // Helper function to wait for appropriate MIME-type headers and
+ // then prompt the user with a file picker
+ saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) {
+ // canonical def in nsURILoader.h
+ const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
+
+ // an object to proxy the data through to
+ // nsIExternalHelperAppService.doContent, which will wait for the
+ // appropriate MIME-type headers and then prompt the user with a
+ // file picker
+ function SaveAsListener() {}
+ SaveAsListener.prototype = {
+ extListener: null,
+
+ onStartRequest: function onStartRequest(aRequest, aContext) {
+ // If the timer fired, the error status will have been caused by that,
+ // and we'll be restarting in onStopRequest, so no reason to notify
+ // the user.
+ if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
+ return;
+
+ clearTimeout(timer);
+
+ // some other error occured; notify the user...
+ if (!Components.isSuccessCode(aRequest.status)) {
+ try {
+ const bundle = Services.strings.createBundle(
+ "chrome://mozapps/locale/downloads/downloads.properties");
+
+ const title = bundle.GetStringFromName("downloadErrorAlertTitle");
+ const msg = bundle.GetStringFromName("downloadErrorGeneric");
+
+ Services.prompt.alert(doc.defaultView, title, msg);
+ } catch (ex) {}
+ return;
+ }
+
+ var extHelperAppSvc =
+ Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
+ .getService(Ci.nsIExternalHelperAppService);
+ var channel = aRequest.QueryInterface(Ci.nsIChannel);
+ this.extListener = extHelperAppSvc.doContent(channel.contentType, aRequest,
+ doc.defaultView, true);
+ this.extListener.onStartRequest(aRequest, aContext);
+ },
+
+ onStopRequest: function onStopRequest(aRequest, aContext, aStatusCode) {
+ if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
+ // Do it the old fashioned way, which will pick the best filename
+ // it can without waiting.
+ saveURL(linkURL, linkText, dialogTitle, bypassCache, true, doc.documentURIObject, doc);
+ }
+ if (this.extListener)
+ this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
+ },
+
+ onDataAvailable: function onDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount) {
+ this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount);
+ }
+ }
+
+ function Callbacks() {}
+ Callbacks.prototype = {
+ getInterface: function getInterface(aIID) {
+ if (aIID.equals(Ci.nsIAuthPrompt) ||
+ aIID.equals(Ci.nsIAuthPrompt2)) {
+ // If the channel demands authentication prompt, we must cancel it
+ // because the save-as-timer would expire and cancel the channel
+ // before we get credentials from user. Both authentication dialog
+ // and save as dialog would appear on the screen as we fall back to
+ // the old fashioned way after the timeout.
+ timer.cancel();
+ channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ }
+
+ // If we don't have the headers after a short time the user won't have
+ // received any feedback from the click. That's bad, so we give up
+ // waiting for the filename.
+ function timerCallback() {
+ channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
+ }
+
+ // set up a channel to do the saving
+ var channel = NetUtil.newChannel({
+ uri: makeURI(linkURL),
+ loadUsingSystemPrincipal: true,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
+ });
+
+ channel.notificationCallbacks = new Callbacks();
+
+ var flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
+
+ if (bypassCache)
+ flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+
+ if (channel instanceof Ci.nsICachingChannel)
+ flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
+
+ channel.loadFlags |= flags;
+
+ if (channel instanceof Ci.nsIPrivateBrowsingChannel)
+ channel.setPrivate(gPrivate);
+
+ if (channel instanceof Ci.nsIHttpChannel) {
+ channel.referrer = doc.documentURIObject;
+ if (channel instanceof Ci.nsIHttpChannelInternal)
+ channel.forceAllowThirdPartyCookie = true;
+ }
+
+ // fallback to the old way if we don't see the headers quickly
+ var timeToWait = Services.prefs.getIntPref("browser.download.saveLinkAsFilenameTimeout");
+ var timer = setTimeout(timerCallback, timeToWait);
+
+ // kick off the channel with our proxy object as the listener
+ channel.asyncOpen2(new SaveAsListener());
+ },
+
+ // Save URL of clicked-on image, video, or audio.
+ saveMedia: function() {
+ var doc = this.target.ownerDocument;
+ let referrerURI = doc.documentURIObject;
+
+ if (this.onCanvas)
+ // Bypass cache, since it's a data: URL.
+ saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
+ true, false, referrerURI, null, null, null,
+ (gPrivate ? true : false),
+ document.nodePrincipal /* system, because blob: */);
+ else if (this.onImage) {
+ urlSecurityCheck(this.mediaURL, this.principal);
+ saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
+ false, referrerURI, null, gContextMenuContentData.contentType,
+ gContextMenuContentData.contentDisposition,
+ (gPrivate ? true : false),
+ this.principal);
+ }
+ else if (this.onVideo || this.onAudio) {
+ urlSecurityCheck(this.mediaURL, this.principal);
+ var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
+ this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
+ }
+ },
+
+ // Backwards-compatibility wrapper
+ saveImage: function() {
+ if (this.onCanvas || this.onImage)
+ this.saveMedia();
+ },
+
+ // Generate email address.
+ getEmail: function() {
+ // Get the comma-separated list of email addresses only.
+ // There are other ways of embedding email addresses in a mailto:
+ // link, but such complex parsing is beyond us.
+ var addresses;
+ try {
+ // Let's try to unescape it using a character set
+ var characterSet = this.target.ownerDocument.characterSet;
+ const textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]
+ .getService(Ci.nsITextToSubURI);
+ addresses = this.linkURL.match(/^mailto:([^?]+)/)[1];
+ addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
+ }
+ catch(ex) {
+ // Do nothing.
+ }
+ return addresses;
+ },
+
+ // Copy email to clipboard
+ copyEmail: function() {
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(this.getEmail());
+ },
+
+ bookmarkThisPage : function() {
+ window.top.PlacesCommandHook.bookmarkPage(this.browser,
+ true);
+ },
+
+ bookmarkLink: function CM_bookmarkLink() {
+ window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
+ this.linkURL,
+ this.linkText());
+ },
+
+ addBookmarkForFrame: function() {
+ var doc = this.target.ownerDocument;
+ var uri = doc.documentURIObject;
+
+ var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+ if (itemId == -1) {
+ var title = doc.title;
+ var description = PlacesUIUtils.getDescriptionFromDocument(doc);
+ PlacesUIUtils.showMinimalAddBookmarkUI(uri, title, description);
+ }
+ else
+ PlacesUIUtils.showItemProperties(itemId,
+ PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ },
+
+ // Open Metadata window for node
+ showMetadata: function() {
+ window.openDialog("chrome://navigator/content/metadata.xul",
+ "_blank",
+ "scrollbars,resizable,chrome,dialog=no",
+ this.target);
+ },
+
+ ///////////////
+ // Utilities //
+ ///////////////
+
+ // Show/hide one item (specified via name or the item element itself).
+ showItem: function(aItemOrId, aShow) {
+ var item = aItemOrId.constructor == String ? document.getElementById(aItemOrId) : aItemOrId;
+ if (item)
+ item.hidden = !aShow;
+ },
+
+ // Set given attribute of specified context-menu item. If the
+ // value is null, then it removes the attribute (which works
+ // nicely for the disabled attribute).
+ setItemAttr: function(aID, aAttr, aVal) {
+ var elem = document.getElementById(aID);
+ if (elem) {
+ if (aVal == null) {
+ // null indicates attr should be removed.
+ elem.removeAttribute(aAttr);
+ }
+ else {
+ // Set attr=val.
+ elem.setAttribute(aAttr, aVal);
+ }
+ }
+ },
+
+ // Set context menu attribute according to like attribute of another node
+ // (such as a broadcaster).
+ setItemAttrFromNode: function(aItem_id, aAttr, aOther_id) {
+ var elem = document.getElementById(aOther_id);
+ if (elem && elem.getAttribute(aAttr) == "true") {
+ this.setItemAttr(aItem_id, aAttr, "true");
+ }
+ else {
+ this.setItemAttr(aItem_id, aAttr, null);
+ }
+ },
+
+ // Temporary workaround for DOM api not yet implemented by XUL nodes.
+ cloneNode: function(aItem) {
+ // Create another element like the one we're cloning.
+ var node = document.createElement(aItem.tagName);
+
+ // Copy attributes from argument item to the new one.
+ var attrs = aItem.attributes;
+ for (var i = 0; i < attrs.length; i++) {
+ var attr = attrs.item(i);
+ node.setAttribute(attr.nodeName, attr.nodeValue);
+ }
+
+ // Voila!
+ return node;
+ },
+
+ // Generate fully qualified URL for clicked-on link.
+ getLinkURL: function() {
+ if (this.link.href)
+ return this.link.href;
+
+ var href;
+ if (this.link.namespaceURI == "http://www.w3.org/1998/Math/MathML")
+ href = this.link.getAttribute("href");
+
+ if (!href)
+ href = this.link.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+
+ if (!href || !href.match(/\S/)) {
+ // Without this we try to save as the current doc,
+ // for example, HTML case also throws if empty
+ throw "Empty href";
+ }
+
+ return makeURLAbsolute(this.link.baseURI, href);
+ },
+
+ getLinkURI: function() {
+ try {
+ return makeURI(this.linkURL);
+ }
+ catch (ex) {
+ // e.g. empty URL string
+ }
+
+ return null;
+ },
+
+ getLinkProtocol: function() {
+ if (this.linkURI)
+ return this.linkURI.scheme; // can be |undefined|
+
+ return null;
+ },
+
+ // Get text of link.
+ linkText: function() {
+ var text = gatherTextUnder(this.link);
+ if (text && text.match(/\S/))
+ return text;
+
+ text = this.link.getAttribute("title");
+ if (text && text.match(/\S/))
+ return text;
+
+ text = this.link.getAttribute("alt");
+ if (text && text.match(/\S/))
+ return text;
+
+ if (this.link.href)
+ return this.link.href;
+
+ if (elem.namespaceURI == "http://www.w3.org/1998/Math/MathML")
+ text = elem.getAttribute("href");
+ if (!text || !text.match(/\S/))
+ text = elem.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+ if (text && text.match(/\S/))
+ return makeURLAbsolute(this.link.baseURI, text);
+
+ return null;
+ },
+
+ /**
+ * Determines whether the focused window has selected text, and if so
+ * formats the first 15 characters for the label of the context-searchselect
+ * element according to the searchText string.
+ * @return true if there is selected text, false if not
+ */
+ isTextSelection: function() {
+ var searchSelectText = this.searchSelected(16);
+
+ if (!searchSelectText)
+ return false;
+
+ if (searchSelectText.length > 15)
+ searchSelectText = searchSelectText.substr(0, 15) + this.ellipsis;
+
+ // Use the current engine if it's a browser window and the search bar is
+ // visible, the default engine otherwise.
+ var engineName = "";
+ if (window.BrowserSearch &&
+ (isElementVisible(BrowserSearch.searchBar) ||
+ BrowserSearch.searchSidebar))
+ engineName = Services.search.currentEngine.name;
+ else
+ engineName = Services.search.defaultEngine.name;
+
+ // format "Search <engine> for <selection>" string to show in menu
+ const bundle = document.getElementById("contentAreaCommandsBundle");
+ var menuLabel = bundle.getFormattedString("searchSelected",
+ [engineName, searchSelectText]);
+ this.setItemAttr("context-searchselect", "label", menuLabel);
+ this.setItemAttr("context-searchselect", "accesskey",
+ bundle.getString("searchSelected.accesskey"));
+
+ return true;
+ },
+
+ searchSelected: function(aCharlen) {
+ var focusedWindow = document.commandDispatcher.focusedWindow;
+ var searchStr = focusedWindow.getSelection();
+ searchStr = searchStr.toString();
+
+ if (this.onTextInput) {
+ var fElem = this.target;
+ if ((fElem instanceof HTMLInputElement &&
+ fElem.mozIsTextField(true)) ||
+ fElem instanceof HTMLTextAreaElement) {
+ searchStr = fElem.value.substring(fElem.selectionStart, fElem.selectionEnd);
+ }
+ }
+
+ // searching for more than 150 chars makes no sense
+ if (!aCharlen)
+ aCharlen = 150;
+ if (aCharlen < searchStr.length) {
+ // only use the first charlen important chars. see bug 221361
+ var pattern = new RegExp("^(?:\\s*.){0," + aCharlen + "}");
+ pattern.test(searchStr);
+ searchStr = RegExp.lastMatch;
+ }
+
+ return searchStr.trim().replace(/\s+/g, " ");
+ },
+
+ // Returns true if anything is selected.
+ isContentSelection: function() {
+ return !document.commandDispatcher.focusedWindow.getSelection().isCollapsed;
+ },
+
+ // Returns true if the target is editable
+ isTargetEditable: function() {
+ if (this.target.ownerDocument.designMode == "on")
+ return true;
+
+ for (var node = this.target; node; node = node.parentNode)
+ if (node.nodeType == node.ELEMENT_NODE &&
+ node.namespaceURI == "http://www.w3.org/1999/xhtml")
+ return node.isContentEditable;
+ return false;
+ },
+
+ toString: function() {
+ return "contextMenu.target = " + this.target + "\n" +
+ "contextMenu.onImage = " + this.onImage + "\n" +
+ "contextMenu.onLink = " + this.onLink + "\n" +
+ "contextMenu.link = " + this.link + "\n" +
+ "contextMenu.inFrame = " + this.inFrame + "\n" +
+ "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
+ },
+
+ isTextBoxEnabled: function(aNode) {
+ return !aNode.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .isNodeDisabledForEvents(aNode);
+ },
+
+ isTargetATextBox: function(aNode) {
+ if (aNode instanceof HTMLInputElement)
+ return aNode.mozIsTextField(false) && this.isTextBoxEnabled(aNode);
+
+ return aNode instanceof HTMLTextAreaElement && this.isTextBoxEnabled(aNode);
+ },
+
+ /**
+ * Determine whether a separator should be shown based on whether
+ * there are any non-hidden items between it and the previous separator.
+ * @param aSeparatorID
+ * The id of the separator element
+ * @return true if the separator should be shown, false if not
+ */
+ shouldShowSeparator: function(aSeparatorID) {
+ let separator = document.getElementById(aSeparatorID);
+ if (separator) {
+ let sibling = separator.previousSibling;
+ while (sibling && sibling.localName != "menuseparator") {
+ if (sibling.getAttribute("hidden") != "true")
+ return true;
+ sibling = sibling.previousSibling;
+ }
+ }
+ return false;
+ },
+
+ mediaCommand: function(aCommand, aData) {
+ var media = this.target;
+
+ switch (aCommand) {
+ case "play":
+ media.play();
+ break;
+ case "pause":
+ media.pause();
+ break;
+ case "loop":
+ media.loop = !media.loop;
+ break;
+ case "mute":
+ media.muted = true;
+ break;
+ case "unmute":
+ media.muted = false;
+ break;
+ case "playbackRate":
+ media.playbackRate = aData;
+ break;
+ case "hidecontrols":
+ media.removeAttribute("controls");
+ break;
+ case "showcontrols":
+ media.setAttribute("controls", "true");
+ break;
+ case "showstats":
+ case "hidestats":
+ var win = media.ownerDocument.defaultView;
+ var showing = aCommand == "showstats";
+ media.dispatchEvent(new win.CustomEvent("media-showStatistics",
+ { bubbles: false, cancelable: true, detail: showing }));
+ break;
+ }
+ },
+
+ copyMediaLocation: function() {
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(this.mediaURL);
+ },
+
+ get imageURL() {
+ if (this.onImage)
+ return this.mediaURL;
+ return "";
+ }
+};
+
+XPCOMUtils.defineLazyGetter(nsContextMenu.prototype, "ellipsis", function() {
+ return Services.prefs.getComplexValue("intl.ellipsis",
+ Ci.nsIPrefLocalizedString).data;
+});
diff --git a/comm/suite/base/content/openLocation.js b/comm/suite/base/content/openLocation.js
new file mode 100644
index 0000000000..2bb9524d94
--- /dev/null
+++ b/comm/suite/base/content/openLocation.js
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; 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/. */
+
+var gInput;
+var gAcceptButton;
+var gLastPref = "general.open_location.last_url";
+var gOpenAppList;
+var gBundle;
+var gAction;
+
+function onLoad()
+{
+ gInput = document.getElementById("dialog.input");
+ gAcceptButton = document.documentElement.getButton("accept");
+ gOpenAppList = document.getElementById("openAppList");
+ gBundle = document.getElementById("openLocationBundle");
+ gAction = window.arguments[0].action;
+ // Set arguments action to prevent problems on cancel.
+ window.arguments[0].action = "-1";
+
+ switch (gAction) {
+ case "5": // attach web page
+ document.title = gBundle.getString("attachTitle");
+ document.getElementById("enterLabel").value = gBundle.getString("attachEnterLabel");
+ document.getElementById("openWhereBox").setAttribute("hidden", true);
+
+ // Change accept button text to 'attach'.
+ gAcceptButton.label = gBundle.getString("attachButtonLabel");
+ gLastPref = "mailnews.attach_web_page.last_url";
+
+ break;
+
+ case "2": // open web page from composer
+ gOpenAppList.selectedItem = document.getElementById("editWindow");
+ var openTopWindow = document.getElementById("currentTab");
+
+ // Change string to make more sense for Composer.
+ openTopWindow.setAttribute("label",
+ gBundle.getString("existingNavigatorWindow"));
+
+ // Disable existing browser and new tab menuitems and create indicator
+ // if no browser windows found.
+ if (!Services.wm.getMostRecentWindow("navigator:browser")) {
+ openTopWindow.setAttribute("disabled", "true");
+ document.getElementById("newTab").setAttribute("disabled", "true");
+ gAction = "-1";
+ }
+ break;
+
+ default: // open web page
+ gOpenAppList.value = Services.prefs.getIntPref("general.open_location.last_window_choice");
+ }
+
+ gInput.value = Services.prefs.getStringPref(gLastPref, "");
+ if (gInput.value)
+ gInput.select(); // XXX should probably be done automatically
+
+ doEnabling();
+}
+
+function doEnabling()
+{
+ gAcceptButton.disabled = !gInput.value;
+}
+
+function accept()
+{
+ var params = window.arguments[0];
+ params.url = gInput.value;
+ params.action = gOpenAppList.value;
+ if (gAction == "4" || params.action == "4")
+ return; // private, don't set any preferences
+
+ if (gAction != "5") { // open web page
+ // If there were no browser windows open and not set to open in composer
+ // then set to open in a new window.
+ if (gAction == "-1" && params.action != "2")
+ params.action = "1";
+
+ // If open web page from navigator window, save last window choice.
+ if (gAction == "0")
+ Services.prefs.setIntPref("general.open_location.last_window_choice",
+ gOpenAppList.value);
+ }
+
+ SetStringPref(gLastPref, gInput.value);
+}
+
+function onChooseFile()
+{
+ const nsIFilePicker = Ci.nsIFilePicker;
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ fp.init(window, gBundle.getString("chooseFileDialogTitle"),
+ nsIFilePicker.modeOpen);
+ if (window.arguments[0].action != "5" && gOpenAppList.value == "2") {
+ // When loading into Composer, direct user to prefer HTML files and text
+ // files, so we call separately to control the order of the filter list.
+ fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ } else {
+ fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText |
+ nsIFilePicker.filterAll | nsIFilePicker.filterImages |
+ nsIFilePicker.filterXML);
+ }
+
+ fp.open(rv => {
+ if (rv == nsIFilePicker.returnOK && fp.fileURL.spec &&
+ fp.fileURL.spec.length > 0) {
+ gInput.value = fp.fileURL.spec;
+ }
+
+ doEnabling();
+ });
+}
+
+function useUBHistoryItem(aValue)
+{
+ gInput.value = aValue;
+ gInput.focus();
+ doEnabling();
+}
diff --git a/comm/suite/base/content/openLocation.xul b/comm/suite/base/content/openLocation.xul
new file mode 100644
index 0000000000..1116f76562
--- /dev/null
+++ b/comm/suite/base/content/openLocation.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % openDialogDTD SYSTEM "chrome://communicator/locale/openLocation.dtd" >
+ %openDialogDTD;
+]>
+
+<dialog id="openLocation"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&caption.label;"
+ onload="onLoad()"
+ ondialogaccept="return accept();"
+ buttonlabelaccept="&open.label;"
+ style="width: 40em;"
+ persist="screenX screenY"
+ screenX="24" screenY="24">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/openLocation.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://navigator/content/sessionHistoryUI.js"/>
+
+ <stringbundle id="openLocationBundle" src="chrome://communicator/locale/openLocation.properties"/>
+
+ <hbox>
+ <separator orient="vertical" class="thin"/>
+ <vbox flex="1">
+ <label id="enterLabel"
+ value="&enter.label;"
+ control="dialog.input"
+ accesskey="&enter.accesskey;"/>
+ <separator class="thin"/>
+
+ <hbox align="center">
+ <textbox id="dialog.input" flex="1" type="autocomplete"
+ autocompletesearch="history file" timeout="50" maxrows="6"
+ enablehistory="true" class="uri-element"
+ oninput="doEnabling();">
+ <menupopup id="ubhist-popup" class="autocomplete-history-popup"
+ popupalign="topleft" popupanchor="bottomleft"
+ onpopupshowing="createUBHistoryMenu(event.target);"
+ oncommand="useUBHistoryItem(event.target.label);"/>
+ </textbox>
+ <button label="&chooseFile.label;" accesskey="&chooseFile.accesskey;" oncommand="onChooseFile();"/>
+ </hbox>
+ <hbox id="openWhereBox" align="center">
+ <label value="&openWhere.label;" accesskey="&openWhere.accesskey;" control="openAppList"/>
+ <menulist id="openAppList">
+ <menupopup>
+ <menuitem value="0"
+ id="currentTab"
+ label="&currentTab.label;"
+ selected="true"/>
+ <menuitem value="3" id="newTab" label="&newTab.label;"/>
+ <menuitem value="1" id="newWindow" label="&newWindow.label;"/>
+ <menuitem value="4" id="newPrivate" label="&newPrivate.label;"/>
+ <menuseparator/>
+ <menuitem value="2" id="editWindow" label="&editNewWindow.label;"/>
+ </menupopup>
+ </menulist>
+ <spacer flex="1"/>
+ </hbox>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/base/content/overrides/app-license.html b/comm/suite/base/content/overrides/app-license.html
new file mode 100644
index 0000000000..ca69723fa8
--- /dev/null
+++ b/comm/suite/base/content/overrides/app-license.html
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+
+ <p><b>Binaries</b> of this product have been made available to you by the
+ <a href="http://www.seamonkey-project.org/">SeaMonkey Project</a>
+ under the Mozilla Public License 2.0 (MPL).
+ <a href="about:rights">Know your rights</a>.</p>
diff --git a/comm/suite/base/content/safeMode.js b/comm/suite/base/content/safeMode.js
new file mode 100644
index 0000000000..7572bfef8b
--- /dev/null
+++ b/comm/suite/base/content/safeMode.js
@@ -0,0 +1,92 @@
+/* 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/. */
+
+const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const appStartup = Services.startup;
+
+function restartApp() {
+ appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
+}
+
+function clearAllPrefs() {
+ Services.prefs.resetUserPrefs();
+
+ // Remove the pref-overrides dir, if it exists.
+ try {
+ var prefOverridesDir = Services.dirsvc.get("PrefDOverride", Ci.nsIFile);
+ prefOverridesDir.remove(true);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+}
+
+function restoreDefaultBookmarks() {
+ Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", true);
+}
+
+function deleteLocalstore() {
+ // Delete the xulstore file.
+ let xulstoreFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ xulstoreFile.append("xulstore.json");
+ if (xulstoreFile.exists())
+ xulstoreFile.remove(false);
+}
+
+function disableAddons() {
+ AddonManager.getAllAddons(function(aAddons) {
+ aAddons.forEach(function(aAddon) {
+ if (aAddon.type == "theme") {
+ // Setting userDisabled to false on the default theme activates it,
+ // disables all other themes and deactivates the applied persona, if
+ // any.
+ const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
+ if (aAddon.id == DEFAULT_THEME_ID)
+ aAddon.userDisabled = false;
+ }
+ else {
+ aAddon.userDisabled = true;
+ }
+ });
+
+ restartApp();
+ });
+}
+
+function onOK() {
+ try {
+ if (document.getElementById("resetUserPrefs").checked)
+ clearAllPrefs();
+ if (document.getElementById("deleteBookmarks").checked)
+ restoreDefaultBookmarks();
+ if (document.getElementById("resetToolbars").checked)
+ deleteLocalstore();
+ if (document.getElementById("restoreSearch").checked)
+ Services.search.restoreDefaultEngines();
+ if (document.getElementById("disableAddons").checked) {
+ disableAddons();
+ // disableAddons will asynchronously restart the application
+ return false;
+ }
+ } catch(e) {
+ }
+
+ restartApp();
+ return false;
+}
+
+function onCancel() {
+ appStartup.quit(appStartup.eForceQuit);
+ return false;
+}
+
+function onLoad() {
+ document.documentElement.getButton("extra1").focus();
+}
+
+function UpdateOKButtonState() {
+ document.documentElement.getButton("accept").disabled =
+ !document.getElementsByAttribute("checked", "true").item(0);
+}
diff --git a/comm/suite/base/content/safeMode.xul b/comm/suite/base/content/safeMode.xul
new file mode 100644
index 0000000000..76b874c783
--- /dev/null
+++ b/comm/suite/base/content/safeMode.xul
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+
+<!-- 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 prefwindow [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % safeModeDTD SYSTEM "chrome://communicator/locale/safeMode.dtd">
+%safeModeDTD;
+<!ENTITY % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<dialog id="safeModeDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&safeModeDialog.title;"
+ buttons="accept,cancel,extra1"
+ buttonlabelaccept="&changeAndRestartButton.label;"
+ buttonlabelcancel="&quitApplicationCmd.label;"
+ buttonlabelextra1="&continueButton.label;"
+ width="&window.width;"
+ ondialogaccept="return onOK();"
+ ondialogcancel="return onCancel();"
+ ondialogextra1="window.close();"
+ onload="onLoad();"
+ buttondisabledaccept="true">
+
+ <script src="chrome://communicator/content/safeMode.js"/>
+
+ <description>&safeModeDescription.label;</description>
+
+ <separator class="thin"/>
+
+ <label value="&safeModeDescription2.label;"/>
+ <vbox id="tasks" oncommand="UpdateOKButtonState();">
+ <checkbox id="disableAddons"
+ label="&disableAddons.label;"
+ accesskey="&disableAddons.accesskey;"/>
+ <checkbox id="resetToolbars"
+ label="&resetToolbars.label;"
+ accesskey="&resetToolbars.accesskey;"/>
+ <checkbox id="deleteBookmarks"
+ label="&deleteBookmarks.label;"
+ accesskey="&deleteBookmarks.accesskey;"/>
+ <checkbox id="resetUserPrefs"
+ label="&resetUserPrefs.label;"
+ accesskey="&resetUserPrefs.accesskey;"/>
+ <checkbox id="restoreSearch"
+ label="&restoreSearch.label;"
+ accesskey="&restoreSearch.accesskey;"/>
+ </vbox>
+
+ <separator class="thin"/>
+</dialog>
diff --git a/comm/suite/base/content/tasksOverlay.js b/comm/suite/base/content/tasksOverlay.js
new file mode 100644
index 0000000000..49ec7c03af
--- /dev/null
+++ b/comm/suite/base/content/tasksOverlay.js
@@ -0,0 +1,276 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function toNavigator()
+{
+ if (!CycleWindow("navigator:browser"))
+ OpenBrowserWindow();
+}
+
+function ExpirePassword()
+{
+ // Queries the HTTP Auth Manager and clears all sessions
+ Cc['@mozilla.org/network/http-auth-manager;1']
+ .getService(Ci.nsIHttpAuthManager)
+ .clearAll();
+
+ // Expires the master password
+ Cc["@mozilla.org/security/pk11tokendb;1"]
+ .createInstance(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken()
+ .checkPassword("");
+}
+
+function toDownloadManager()
+{
+ Cc["@mozilla.org/suite/suiteglue;1"]
+ .getService(Ci.nsISuiteGlue)
+ .showDownloadManager();
+}
+
+function toDataManager(aView)
+{
+ var useDlg = Services.prefs.getBoolPref("suite.manager.dataman.openAsDialog");
+
+ if (useDlg) {
+ var url = "chrome://communicator/content/dataman/dataman.xul";
+ var win = toOpenWindowByType("data:manager", url, "", aView);
+ if (win && aView)
+ win.gDataman.loadView(aView);
+ return;
+ }
+
+ switchToTabHavingURI("about:data", true, function(browser) {
+ if (aView)
+ browser.contentWindow.wrappedJSObject.gDataman.loadView(aView);
+ });
+}
+
+function toEM(aView)
+{
+ var useDlg = Services.prefs.getBoolPref("suite.manager.addons.openAsDialog");
+
+ if (useDlg) {
+ var view = aView ? { view: aView } : null;
+ var url = "chrome://mozapps/content/extensions/extensions.xul";
+ var win = toOpenWindowByType("Addons:Manager", url, "", view);
+ if (win && aView)
+ win.loadView(aView);
+ return;
+ }
+
+ switchToTabHavingURI("about:addons", true, function(browser) {
+ if (aView)
+ browser.contentWindow.wrappedJSObject.loadView(aView);
+ });
+}
+
+function toBookmarksManager()
+{
+ toOpenWindowByType("Places:Organizer",
+ "chrome://communicator/content/places/places.xul");
+}
+
+function toJavaScriptConsole()
+{
+ toOpenWindowByType("suite:console", "chrome://communicator/content/console/console.xul");
+}
+
+function toOpenWindow( aWindow )
+{
+ try {
+ // Try to focus the previously focused window e.g. message compose body
+ aWindow.document.commandDispatcher.focusedWindow.focus();
+ } catch (e) {
+ // e.g. non-XUL document; just raise the top window
+ aWindow.focus();
+ }
+}
+
+function toOpenWindowByType(inType, uri, features, args)
+{
+ // don't do several loads in parallel
+ if (uri in window)
+ return;
+
+ var topWindow = Services.wm.getMostRecentWindow(inType);
+ if ( topWindow )
+ {
+ toOpenWindow( topWindow );
+ return topWindow;
+ }
+ else
+ {
+ // open the requested window, but block it until it's fully loaded
+ function newWindowLoaded(event)
+ {
+ // make sure that this handler is called only once
+ window.removeEventListener("unload", newWindowLoaded);
+ window[uri].removeEventListener("load", newWindowLoaded);
+ delete window[uri];
+ }
+
+ // Remember the newly loading window until it's fully loaded
+ // or until the current window passes away.
+ // Only pass args if they exist and have a value (see Bug 1279738).
+ if (typeof args != "undefined" && args) {
+ window[uri] = openDialog(uri, "",
+ features || "non-private,all,dialog=no",
+ args || null);
+ }
+ else {
+ window[uri] = openDialog(uri, "",
+ features || "non-private,all,dialog=no");
+ }
+
+ window[uri].addEventListener("load", newWindowLoaded);
+ window.addEventListener("unload", newWindowLoaded);
+ }
+ return;
+}
+
+function OpenBrowserWindow()
+{
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+ if (document.documentElement.getAttribute("windowtype") ==
+ "navigator:browser" && window.content && window.content.document)
+ {
+ // if and only if the current window is a browser window and
+ // it has a document with a character set, then extract the
+ // current charset menu setting from the current document
+ // and use it to initialize the new browser window
+ return window.openDialog(getBrowserURL(), "_blank",
+ "chrome,all,dialog=no,non-private", null,
+ "charset=" + window.content.document.characterSet);
+ }
+
+ if (win) {
+ // if a browser window already exists then set startpage to null so
+ // navigator.js can check pref for how new window should be opened
+ return win.openDialog(getBrowserURL(), "_blank",
+ "chrome,all,dialog=no,non-private", null);
+ }
+
+ // open the first browser window as if we were starting up
+ var cmdLine = {
+ handleFlagWithParam: function handleFlagWithParam(flag, caseSensitive) {
+ return flag == "remote" ? "xfeDoCommand(openBrowser)" : null;
+ },
+ handleFlag: function handleFlag(flag, caseSensitive) {
+ return false;
+ },
+ preventDefault: true
+ };
+ const clh_prefix = "@mozilla.org/commandlinehandler/general-startup;1";
+ Cc[clh_prefix + "?type=browser"]
+ .getService(Ci.nsICommandLineHandler)
+ .handle(cmdLine);
+ return null;
+}
+
+function CycleWindow(aType) {
+ let topWindowOfType = Services.wm.getMostRecentWindow(aType);
+ if (topWindowOfType == null)
+ return null;
+
+ let topWindow = Services.wm.getMostRecentWindow(null);
+ if (topWindowOfType != topWindow) {
+ toOpenWindow(topWindowOfType);
+ return topWindowOfType;
+ }
+
+ let topFound = false;
+ let enumerator = Services.wm.getEnumerator(aType);
+ let iWindow;
+ let firstWindow;
+
+ while (enumerator.hasMoreElements()) {
+ iWindow = enumerator.getNext();
+ if (!iWindow.closed) {
+ if (!firstWindow) {
+ firstWindow = iWindow;
+ }
+ if (topFound) {
+ toOpenWindow(iWindow);
+ return iWindow;
+ }
+ if (iWindow == topWindow) {
+ topFound = true;
+ }
+ }
+ }
+
+ if (firstWindow == topWindow) // Only one window
+ return null;
+
+ toOpenWindow(firstWindow);
+ return firstWindow;
+}
+
+function windowMenuDidHide()
+{
+ let sep = document.getElementById("sep-window-list");
+ // Clear old items
+ while (sep.nextElementSibling) {
+ sep.nextElementSibling.remove();
+ }
+}
+
+function checkFocusedWindow()
+{
+ let windows = Services.wm.getEnumerator("");
+ let frag = document.createDocumentFragment();
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext();
+ if (win.closed || win.document.documentElement.getAttribute("inwindowmenu") == "false") {
+ continue;
+ }
+ let item = document.createElement("menuitem");
+ item.setAttribute("label", win.document.title);
+ item.setAttribute("type", "radio");
+ if (win == window) {
+ item.setAttribute("checked", "true");
+ }
+ item.addEventListener("command", () => {
+ if (win.windowState == window.STATE_MINIMIZED) {
+ win.restore();
+ }
+ win.focus();
+ });
+ frag.appendChild(item);
+ }
+ document.getElementById("windowPopup").appendChild(frag);
+}
+
+function toProfileManager()
+{
+ var promgrWin = Services.wm.getMostRecentWindow("mozilla:profileSelection");
+ if (promgrWin) {
+ promgrWin.focus();
+ } else {
+ var params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+
+ params.SetNumberStrings(1);
+ params.SetString(0, "menu");
+ window.openDialog("chrome://communicator/content/profile/profileSelection.xul",
+ "",
+ "centerscreen,chrome,titlebar,resizable",
+ params);
+ }
+ // Here, we don't care about the result code
+ // that was returned in the param block.
+}
+
+// This function is only used by macs.
+function ZoomCurrentWindow()
+{
+ if (window.windowState == STATE_NORMAL)
+ window.maximize();
+ else
+ window.restore();
+}
diff --git a/comm/suite/base/content/tasksOverlay.xul b/comm/suite/base/content/tasksOverlay.xul
new file mode 100644
index 0000000000..2835d5302e
--- /dev/null
+++ b/comm/suite/base/content/tasksOverlay.xul
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/tasksOverlay.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % tasksDTD SYSTEM "chrome://communicator/locale/tasksOverlay.dtd" >
+%tasksDTD;
+]>
+
+<overlay id="tasksOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://communicator/content/tasksOverlay.js"/>
+
+<keyset id="tasksKeys">
+#ifdef XP_MACOSX
+ <key id="key_minimizeWindow"
+ command="cmd_minimizeWindow"
+ key="&minimizeWindowCmd.key;"
+ modifiers="accel"/>
+#endif
+ <key id="key_navigator" key="&navigatorCmd.commandkey;" command="Tasks:Navigator" modifiers="accel"/>
+ <key id="key_downloadManager" key="&downloadManagerCmd.commandkey;"
+ command="downloadmgr" modifiers="accel"/>
+ <key id="key_addOnsManager" key="&addOnsManagerCmd.commandkey;"
+ command="addonsmgr" modifiers="accel,shift"/>
+ <key id="key_errorConsole" key="&errorConsoleCmd.commandkey2;"
+ command="Tasks:ErrorConsole" modifiers="accel,alt"/>
+</keyset>
+<commandset id="tasksCommands">
+#ifdef XP_MACOSX
+ <command id="cmd_minimizeWindow" oncommand="window.minimize();"/>
+ <command id="cmd_zoomWindow" oncommand="ZoomCurrentWindow();"/>
+#endif
+ <command id="Tasks:DataMan" oncommand="toDataManager();"/>
+ <command id="Tasks:Navigator" oncommand="toNavigator();"/>
+ <command id="Tasks:ErrorConsole" oncommand="toJavaScriptConsole();"/>
+</commandset>
+
+<broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="sync-setup-state" hidden="true"/>
+ <broadcaster id="sync-syncnow-state" hidden="true"/>
+</broadcasterset>
+
+ <!-- Tasks Menu -->
+ <menu id="tasksMenu" label="&tasksMenu.label;" accesskey="&tasksMenu.accesskey;">
+ <menupopup id="taskPopup">
+ <menuitem id="tasksDataman"
+ label="&datamanCmd.label;"
+ accesskey="&datamanCmd.accesskey;"
+ command="Tasks:DataMan"/>
+ <menu id="menu_passwordManager"
+ label="&passwordManagerCmd.label;"
+ accesskey="&passwordManagerCmd.accesskey;">
+ <menupopup id="passwordPopup">
+ <menuitem label="&passwordDisplayCmd.label;"
+ accesskey="&passwordDisplayCmd.accesskey;"
+ oncommand="toDataManager('|passwords');"/>
+ <menuitem label="&passwordExpireCmd.label;"
+ accesskey="&passwordExpireCmd.accesskey;"
+ oncommand="ExpirePassword();"/>
+ </menupopup>
+ </menu>
+ <menuitem id="downloadmgr" label="&downloadManagerCmd.label;"
+ accesskey="&downloadManagerCmd.accesskey;"
+ key="key_downloadManager" oncommand="toDownloadManager();"/>
+ <menuitem id="addonsmgr" label="&addOnsManagerCmd.label;"
+ accesskey="&addOnsManagerCmd.accesskey;"
+ key="key_addOnsManager" oncommand="toEM();"/>
+ <!-- only one of sync-setup or sync-syncnowitem will be showing at once
+ <menuitem id="sync-setup"
+ label="&syncSetup.label;"
+ accesskey="&syncSetup.accesskey;"
+ observes="sync-setup-state"
+ oncommand="gSyncUI.openSetup();"
+ hidden="true"/>
+ <menuitem id="sync-syncnowitem"
+ label="&syncSyncNowItem.label;"
+ accesskey="&syncSyncNowItem.accesskey;"
+ observes="sync-syncnow-state"
+ oncommand="gSyncUI.doSync(event);"
+ hidden="true"/> -->
+ <menuseparator id="devToolsSeparator"/>
+ <menu label="&webDevelopment.label;" accesskey="&webDevelopment.accesskey;">
+ <menupopup id="toolsPopup">
+ <menuitem id="javascriptConsole" label="&errorConsoleCmd.label;"
+ accesskey="&errorConsoleCmd.accesskey;"
+ key="key_errorConsole" command="Tasks:ErrorConsole"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="sep_switchprofile"/>
+ <menuitem id="cmd_switchprofile" label="&switchProfileCmd.label;" accesskey="&switchProfileCmd.accesskey;" oncommand="toProfileManager();"/>
+ </menupopup>
+ </menu>
+
+ <menu id="windowMenu" label="&windowMenu.label;" accesskey="&windowMenu.accesskey;"
+ onpopupshowing="checkFocusedWindow();" onpopuphidden="windowMenuDidHide();">
+ <menupopup id="windowPopup">
+#ifdef XP_MACOSX
+ <menuitem command="cmd_minimizeWindow"
+ label="&minimizeWindowCmd.label;"
+ key="key_minimizeWindow"
+ position="1"/>
+ <menuitem command="cmd_zoomWindow"
+ label="&zoomWindowCmd.label;"
+ position="2"/>
+ <menuseparator position="3"/>
+#endif
+ <menuitem label="&navigatorCmd.label;" accesskey="&navigatorCmd.accesskey;" key="key_navigator" command="Tasks:Navigator" id="tasksMenuNavigator" class="menuitem-iconic icon-navigator16"/>
+
+ <!-- Overlays get stuffed in here. -->
+ <menuseparator id="sep-window-list"/>
+ </menupopup>
+ </menu>
+
+
+ <statusbarpanel id="component-bar" persist="collapsed">
+ <toolbarbutton class="taskbutton" id="mini-nav" oncommand="toNavigator();"
+ tooltiptext="&taskNavigator.tooltip;"/>
+ </statusbarpanel>
+</overlay>
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);
+ }
+}
diff --git a/comm/suite/base/content/utilityOverlay.xul b/comm/suite/base/content/utilityOverlay.xul
new file mode 100644
index 0000000000..c5d0ea5751
--- /dev/null
+++ b/comm/suite/base/content/utilityOverlay.xul
@@ -0,0 +1,719 @@
+<?xml version="1.0"?>
+<!-- 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 overlay [
+
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+<!ENTITY % customizeToolbarDTD SYSTEM "chrome://communicator/locale/customizeToolbar.dtd">
+%customizeToolbarDTD;
+
+]>
+
+<overlay id="utilityOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://communicator/content/sync/syncUI.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+
+ <stringbundleset>
+ <stringbundle id="bundle_utilityOverlay"
+ src="chrome://communicator/locale/utilityOverlay.properties"/>
+ </stringbundleset>
+
+<broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="cmd_CustomizeToolbars"/>
+</broadcasterset>
+
+ <!-- online/offline status indicators -->
+ <broadcaster id="Communicator:WorkMode"
+ label="&offlineGoOfflineCmd.label;"
+ type="checkbox"
+ oncommand="toggleOfflineStatus();"/>
+
+ <menupopup id="networkProperties" onpopupshown="InitProxyMenu();">
+ <menuitem id="network-proxy-no"
+ type="radio"
+ name="status"
+ label="&direct.label;"
+ accesskey="&direct.accesskey;"
+ oncommand="setNetworkStatus('0')"/>
+ <menuitem id="network-proxy-wpad"
+ type="radio"
+ name="status"
+ label="&wpad.label;"
+ accesskey="&wpad.accesskey;"
+ oncommand="setNetworkStatus('4')"/>
+ <menuitem id="network-proxy-system"
+ type="radio"
+ name="status"
+ label="&system.label;"
+ accesskey="&system.accesskey;"
+ hidden="true"
+ oncommand="setNetworkStatus('5')"/>
+ <menuitem id="network-proxy-pac"
+ type="radio"
+ name="status"
+ label="&pac.label;"
+ accesskey="&pac.accesskey;"
+ oncommand="setNetworkStatus('2')"/>
+ <menuitem id="network-proxy-manual"
+ type="radio"
+ name="status"
+ label="&manual.label;"
+ accesskey="&manual.accesskey;"
+ oncommand="setNetworkStatus('1')"/>
+ <menuseparator/>
+ <menuitem label="&proxy.label;"
+ accesskey="&proxy.accesskey;"
+ oncommand="goPreferences('proxies_pane');"/>
+ </menupopup>
+
+ <menupopup id="toolbar-context-menu"
+ onpopupshowing="onViewToolbarsPopupShowing(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuseparator id="toolbarmode-sep"/>
+ <menu id="toolbarmode-context-menu"
+ label="&customizeToolbar.toolbarmode.label;"
+ accesskey="&customizeToolbar.toolbarmode.accesskey;">
+ <menupopup id="toolbarModePopup"
+ onpopupshowing="onToolbarModePopupShowing(event);"
+ oncommand="goSetToolbarState(event);">
+ <menuitem type="radio" name="mode" value="icons"
+ label="&customizeToolbar.icons.label;"
+ accesskey="&customizeToolbar.icons.accesskey;"/>
+ <menuitem type="radio" name="mode" value="full"
+ label="&customizeToolbar.iconsAndText.label;"
+ accesskey="&customizeToolbar.iconsAndText.accesskey;"/>
+ <menuitem type="radio" name="mode" value="text"
+ label="&customizeToolbar.text.label;"
+ accesskey="&customizeToolbar.text.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="toolbarmode-smallicons"
+ type="checkbox" value="smallicons"
+ label="&customizeToolbar.useSmallIcons.label;"
+ accesskey="&customizeToolbar.useSmallIcons.accesskey;"/>
+ <menuitem id="toolbarmode-labelalign"
+ type="checkbox" value="end"
+ label="&customizeToolbar.labelAlignEnd.label;"
+ accesskey="&customizeToolbar.labelAlignEnd.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="toolbarmode-default"
+ type="checkbox" value="default"
+ label="&customizeToolbar.useDefault.label;"
+ accesskey="&customizeToolbar.useDefault.accesskey;"/>
+ </menupopup>
+ </menu>
+
+ <menuseparator id="toolbar-customize-sep"/>
+ <menuitem id = "customize_toolbars"
+ observes="cmd_CustomizeToolbars"
+ label="&customizeToolbarContext.label;"
+ accesskey="&customizeToolbarContext.accesskey;"
+ oncommand="return SuiteCustomizeToolbar(this);"/>
+
+ </menupopup>
+
+ <panel id="customizeToolbarSheetPopup" noautohide="true">
+ <iframe id="customizeToolbarSheetIFrame"
+ style="&dialog.dimensions;"
+ hidden="true"/>
+ </panel>
+
+ <statusbarpanel id="offline-status" context="networkProperties"
+ observes="Communicator:WorkMode"/>
+
+ <menuitem id="offlineGoOfflineCmd"
+ label="&offlineGoOfflineCmd.label;"
+ accesskey="&offlineGoOfflineCmd.accesskey;"
+ observes="Communicator:WorkMode"/>
+
+ <keyset id="tasksKeys">
+#ifndef XP_MACOSX
+ <key id="key_openHelp"
+ keycode="&openHelpCmd.key;"
+ command="cmd_openHelp"/>
+#else
+ <key id="key_openHelp"
+ key="&openHelpCmdMac.key;"
+ modifiers="&openHelpCmdMac.modifiers;"
+ command="cmd_openHelp"/>
+ <key id="key_preferencesMac"
+ key="&preferencesCmdMac.key;"
+ modifiers="&preferencesCmdMac.modifiers;"/>
+ <key id="key_hideThisApp"
+ key="&hideThisAppCmd.key;"
+ modifiers="&hideThisAppCmd.modifiers;"/>
+ <key id="key_hideOtherApps"
+ key="&hideOtherAppsCmd.key;"
+ modifiers="&hideOtherAppsCmd.modifiers;"/>
+#endif
+ <key id="key_quit"
+ key="&quitApplicationCmd.key;"
+ command="cmd_quit"
+ modifiers="accel"/>
+ </keyset>
+
+ <!-- File Menu -->
+ <menu id="menu_File"
+ label="&fileMenu.label;"
+ accesskey="&fileMenu.accesskey;"/>
+
+ <!-- New SubMenu (Under File Menu) -->
+ <command id="cmd_newNavigator"
+ oncommand="OpenBrowserWindow()"/>
+ <command id="cmd_newPrivateWindow"
+ oncommand="openNewPrivateWith('about:privatebrowsing');"/>
+ <command id="cmd_newEditor"
+ oncommand="NewEditorWindow();"/>
+
+ <!-- XXX not implemented, temporarily disabled
+ <command id="cmd_newEditorTemplate"
+ disabled="true"
+ oncommand="NewEditorFromTemplate();"/>
+ <command id="cmd_newEditorDraft"
+ disabled="true"
+ oncommand="NewEditorFromDraft();"/>
+ -->
+
+ <menuitem id="menu_newEditor"
+ label="&newBlankPageCmd.label;"
+ accesskey="&newBlankPageCmd.accesskey;"
+ key="key_newBlankPage"
+ command="cmd_newEditor"/>
+ <menuitem id="menu_newEditorTemplate"
+ label="&newPageFromTemplateCmd.label;"
+ accesskey="&newPageFromTemplateCmd.accesskey;"
+ command="cmd_newEditorTemplate"/>
+ <menuitem id="menu_newEditorDraft"
+ label="&newPageFromDraftCmd.label;"
+ accesskey="&newPageFromDraftCmd.accesskey;"
+ command="cmd_newEditorDraft"/>
+
+ <menu id="menu_New"
+ label="&newMenu.label;"
+ accesskey="&newMenu.accesskey;"/>
+
+ <menuitem id="menu_newNavigator"
+ label="&newNavigatorCmd.label;"
+ accesskey="&newNavigatorCmd.accesskey;"
+ key="key_newNavigator"
+ command="cmd_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"
+ label="&newPrivateWindowCmd.label;"
+ accesskey="&newPrivateWindowCmd.accesskey;"
+ key="key_newPrivateWindow"
+ command="cmd_newPrivateWindow"/>
+ <menuitem id="menu_close"
+ label="&closeCmd.label;"
+ accesskey="&closeCmd.accesskey;"
+ key="key_close"
+ command="cmd_close"/>
+ <menuitem id="menu_printSetup"
+ label="&printSetupCmd.label;"
+ accesskey="&printSetupCmd.accesskey;"
+ command="cmd_printSetup"/>
+ <menuitem id="menu_printPreview"
+ label="&printPreviewCmd.label;"
+ accesskey="&printPreviewCmd.accesskey;"
+#ifdef XP_MACOSX
+ hidden="true"
+#endif
+ command="cmd_printpreview"/>
+ <menuitem id="menu_print"
+ label="&printCmd.label;"
+ accesskey="&printCmd.accesskey;"
+ key="key_print"
+ command="cmd_print"/>
+
+ <key id="key_newBlankPage"
+ key="&newBlankPageCmd.key;"
+ command="cmd_newEditor"
+ modifiers="accel, shift"/>
+ <key id="key_newNavigator"
+ key="&newNavigatorCmd.key;"
+ command="cmd_newNavigator"
+ modifiers="accel"/>
+ <key id="key_newPrivateWindow"
+ key="&newPrivateWindowCmd.key;"
+ command="cmd_newPrivateWindow"
+ modifiers="accel, shift"/>
+ <key id="key_close"
+ key="&closeCmd.key;"
+ command="cmd_close"
+ modifiers="accel"/>
+ <key id="key_closeWindow"
+ key="&closeCmd.key;"
+ command="cmd_closeWindow"
+ modifiers="accel,shift"/>
+ <key id="key_print"
+ key="&printCmd.key;"
+ command="cmd_print"
+ modifiers="accel"/>
+ <key id="printKb"
+ key="&printCmd.key;"/>
+
+ <menupopup id="menu_FilePopup">
+#ifdef XP_MACOSX
+ <menuitem id="menu_mac_services"
+ label="&servicesMenu.label;"/>
+ <menuitem id="menu_mac_hide_app"
+ label="&hideThisAppCmd.label;"
+ key="key_hideThisApp"/>
+ <menuitem id="menu_mac_hide_others"
+ label="&hideOtherAppsCmd.label;"
+ key="key_hideOtherApps"/>
+ <menuitem id="menu_mac_show_all"
+ label="&showAllAppsCmd.label;"/>
+#else
+ <menuseparator id="menu_FileQuitSeparator"/>
+#endif
+ <menuitem id="menu_FileQuitItem"
+#ifdef XP_WIN
+ label="&quitApplicationCmd.label;"
+ accesskey="&quitApplicationCmd.accesskey;"
+#else
+#ifdef XP_MACOSX
+ label="&quitApplicationCmdMac.label;"
+ accesskey="&quitApplicationCmdMac.accesskey;"
+#else
+ label="&quitApplicationCmdUnix.label;"
+ accesskey="&quitApplicationCmdUnix.accesskey;"
+#endif
+#endif
+ key="key_quit"
+ command="cmd_quit"/>
+ </menupopup>
+
+ <keyset id="findKeys">
+ <key id="key_find"
+ key="&findBarCmd.key;"
+ command="cmd_find"
+ modifiers="accel"/>
+#ifndef XP_MACOSX
+#ifdef XP_UNIX
+ <key keycode="&findCmd.key2;"
+ command="cmd_find"/>
+#endif
+ <key id="key_findReplace"
+ key="&findReplaceCmd.key;"
+ modifiers="accel"
+ command="cmd_findReplace"/>
+#else
+ <key id="key_findReplace"
+ key="&findReplaceCmdMac.key;"
+ modifiers="accel,alt"
+ command="cmd_findReplace"/>
+#endif
+ <key id="key_findNext"
+ key="&findAgainCmd.key;"
+ command="cmd_findNext"
+ modifiers="accel"/>
+ <key id="key_findPrev"
+ key="&findPrevCmd.key;"
+ command="cmd_findPrev"
+ modifiers="accel, shift"/>
+ <key keycode="&findAgainCmd.key2;"
+ command="cmd_findNext"/>
+ <key keycode="&findPrevCmd.key2;"
+ command="cmd_findPrev"
+ modifiers="shift"/>
+#ifndef XP_MACOSX
+ <key id="key_findTypeText"
+ key="&findTypeTextCmd.key;"/>
+ <key id="key_findTypeLinks"
+ key="&findTypeLinksCmd.key;"/>
+#endif
+ </keyset>
+
+ <!-- Edit Menu -->
+ <menu id="menu_Edit"
+ label="&editMenu.label;"
+ accesskey="&editMenu.accesskey;"/>
+ <menuitem id="menu_undo"
+ key="key_undo"
+ accesskey="&undoCmd.accesskey;"
+ command="cmd_undo"/>
+ <menuitem id="menu_redo"
+ key="key_redo"
+ accesskey="&redoCmd.accesskey;"
+ command="cmd_redo"/>
+ <menuitem id="menu_cut"
+ key="key_cut"
+ accesskey="&cutCmd.accesskey;"
+ command="cmd_cut"/>
+ <menuitem id="menu_copy"
+ key="key_copy"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"/>
+ <menuitem id="menu_paste"
+ key="key_paste"
+ accesskey="&pasteCmd.accesskey;"
+ command="cmd_paste"/>
+ <menuitem id="menu_delete"
+ key="key_delete"
+ accesskey="&deleteCmd.accesskey;"
+ command="cmd_delete"/>
+ <menuitem id="menu_selectAll"
+ label="&selectAllCmd.label;"
+ key="key_selectAll"
+ accesskey="&selectAllCmd.accesskey;"
+ command="cmd_selectAll"/>
+ <menuitem id="menu_find"
+ accesskey="&findBarCmd.accesskey;"
+ key="key_find"
+ command="cmd_find"/>
+ <menuitem id="menu_findReplace"
+ accesskey="&findReplaceCmd.accesskey;"
+ key="key_findReplace"
+ command="cmd_findReplace"/>
+ <menuitem id="menu_findNext"
+ label="&findAgainCmd.label;"
+ accesskey="&findAgainCmd.accesskey;"
+ key="key_findNext"
+ command="cmd_findNext"/>
+ <menuitem id="menu_findPrev"
+ label="&findPrevCmd.label;"
+ accesskey="&findPrevCmd.accesskey;"
+ key="key_findPrev"
+ command="cmd_findPrev"/>
+ <menuitem id="menu_findTypeText"
+ label="&findTypeTextCmd.label;"
+ key="key_findTypeText"
+ accesskey="&findTypeTextCmd.accesskey;"
+ command="cmd_findTypeText"/>
+ <menuitem id="menu_findTypeLinks"
+ label="&findTypeLinksCmd.label;"
+ key="key_findTypeLinks"
+ accesskey="&findTypeLinksCmd.accesskey;"
+ command="cmd_findTypeLinks"/>
+ <menuitem id="textfieldDirection-swap"
+ label="&bidiSwitchTextDirectionItem.label;"
+ key="key_switchTextDirection"
+ accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+ command="cmd_switchTextDirection"/>
+
+ <!-- Context Menu Overlay -->
+ <menuitem id="context-undo"
+ accesskey="&undoCmd.accesskey;"
+ command="cmd_undo"/>
+ <menuitem id="context-redo"
+ accesskey="&redoCmd.accesskey;"
+ command="cmd_redo"/>
+ <menuitem id="context-cut"
+ accesskey="&cutCmd.accesskey;"
+ command="cmd_cut"/>
+ <menuitem id="context-copy"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"/>
+ <menuitem id="context-paste"
+ accesskey="&pasteCmd.accesskey;"
+ command="cmd_paste"/>
+ <menuitem id="context-delete"
+ accesskey="&deleteCmd.accesskey;"
+ command="cmd_delete"/>
+ <menuitem id="context-selectall"
+ label="&selectAllCmd.label;"
+ accesskey="&selectAllCmd.accesskey;"
+ command="cmd_selectAll"/>
+
+ <!-- These key nodes are here only for show. The real bindings come from
+ XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->
+
+ <key id="key_undo"
+ key="&undoCmd.key;"
+ modifiers="accel"/>
+ <key id="key_cut"
+ key="&cutCmd.key;"
+ modifiers="accel"/>
+ <key id="key_copy"
+ key="&copyCmd.key;"
+ modifiers="accel"/>
+ <key id="key_paste"
+ key="&pasteCmd.key;"
+ modifiers="accel"/>
+ <key id="key_switchTextDirection"
+ command="cmd_switchTextDirection"
+ key="&bidiSwitchTextDirectionItem.commandkey;"
+ modifiers="accel,shift"/>
+
+#ifndef XP_MACOSX
+ <key id="key_redo"
+ key="&redoCmd.key;"
+ modifiers="accel"/>
+ <key id="key_delete"
+ keycode="VK_DELETE"
+ command="cmd_delete"/>
+#ifdef XP_WIN
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="accel"/>
+#else
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="alt"/>
+#endif
+#else
+ <key id="key_redo"
+ key="&redoCmdMac.key;"
+ modifiers="shift, accel"/>
+ <!-- not all Mac keyboards have a VK_DELETE key, so we use VK_BACK as
+ the primary and provide VK_DELETE as a secondary key definition -->
+ <key id="key_delete"
+ keycode="VK_BACK"
+ command="cmd_delete"/>
+ <key id="key_delete2"
+ keycode="VK_DELETE"
+ command="cmd_delete"/>
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="accel"/>
+#endif
+
+ <commandset id="globalEditMenuItems"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="goUpdateGlobalEditMenuItems()"/>
+ <commandset id="selectEditMenuItems"
+ commandupdater="true"
+ events="select"
+ oncommandupdate="goUpdateSelectEditMenuItems()"/>
+ <commandset id="undoEditMenuItems"
+ commandupdater="true"
+ events="undo"
+ oncommandupdate="goUpdateUndoEditMenuItems()"/>
+ <commandset id="clipboardEditMenuItems"
+ commandupdater="true"
+ events="clipboard"
+ oncommandupdate="goUpdatePasteMenuItems()"/>
+ <commandset id="findTypeMenuItems"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="goUpdateFindTypeMenuItems()"/>
+
+ <commandset id="tasksCommands">
+ <command id="cmd_quit" oncommand="goQuitApplication();"/>
+ <command id="cmd_openHelp"
+ oncommand="openHelp('welcome', 'chrome://communicator/locale/help/suitehelp.rdf');"/>
+ </commandset>
+
+ <command id="cmd_copyLink"
+ oncommand="goDoCommand('cmd_copyLink')"
+ disabled="false"/>
+ <command id="cmd_copyImage"
+ oncommand="CopyImage();"
+ disabled="false"/>
+ <command id="cmd_undo"
+ label="&undoCmd.label;"
+ oncommand="goDoCommand('cmd_undo')"
+ disabled="true"/>
+ <command id="cmd_redo"
+ label="&redoCmd.label;"
+ oncommand="goDoCommand('cmd_redo')"
+ disabled="true"/>
+ <command id="cmd_cut"
+ label="&cutCmd.label;"
+ oncommand="goDoCommand('cmd_cut')"
+ disabled="true"/>
+ <command id="cmd_copy"
+ label="&copyCmd.label;"
+ oncommand="goDoCommand('cmd_copy')"
+ disabled="true"/>
+ <command id="cmd_paste"
+ label="&pasteCmd.label;"
+ oncommand="goDoCommand('cmd_paste')"
+ disabled="true"/>
+ <command id="cmd_delete"
+ label="&deleteCmd.label;"
+ oncommand="goDoCommand('cmd_delete')"
+ valueDefault="&deleteCmd.label;"
+ valueDefaultAccessKey="&deleteCmd.accesskey;"
+ disabled="true"/>
+ <command id="cmd_selectAll"
+ oncommand="goDoCommand('cmd_selectAll')"
+ disabled="true"/>
+ <command id="cmd_switchTextDirection"
+ oncommand="goDoCommand('cmd_switchTextDirection');"/>
+ <command id="cmd_findTypeText"
+ oncommand="findTextAsYouType();"/>
+ <command id="cmd_findTypeLinks"
+ oncommand="findLinksAsYouType();"/>
+
+ <!-- Not needed yet, window will need this: -->
+ <!-- broadcaster id="cmd_preferences"/ -->
+
+#ifndef XP_MACOSX
+ <menuitem id="menu_preferences"
+ label="&preferencesCmd.label;"
+ accesskey="&preferencesCmd.accesskey;"/>
+#else
+ <menuitem id="menu_preferences"
+ label="&preferencesCmdMac.label;"
+ key="key_preferencesMac"/>
+#endif
+
+ <!-- View Menu -->
+ <menu id="menu_View"
+ label="&viewMenu.label;"
+ accesskey="&viewMenu.accesskey;"/>
+ <menu id="menu_Toolbars"
+ label="&viewToolbarsMenu.label;"
+ accesskey="&viewToolbarsMenu.accesskey;"/>
+
+ <menuitem id="menu_showTaskbar"
+ label="&showTaskbarCmd.label;"
+ accesskey="&showTaskbarCmd.accesskey;"
+ oncommand="goToggleToolbar('status-bar', 'menu_showTaskbar')"
+ type="checkbox"
+ checked="true"/>
+
+ <!-- Help Menu -->
+#ifndef XP_WIN
+ <menu id="menu_Help"
+ label="&helpMenu.label;"
+ accesskey="&helpMenu.accesskey;">
+#else
+ <menu id="menu_Help"
+ label="&helpMenuWin.label;"
+ accesskey="&helpMenuWin.accesskey;">
+#endif
+ <menupopup id="helpPopup" onpopupshowing="updateCheckUpdatesItem();">
+#ifndef XP_MACOSX
+ <menuitem label="&openHelpCmd.label;"
+ accesskey="&openHelpCmd.accesskey;"
+ id="help"
+ key="key_openHelp"
+ command="cmd_openHelp"/>
+#else
+ <menuitem label="&openHelpCmdMac.label;"
+ accesskey="&openHelpCmdMac.accesskey;"
+ id="help"
+ key="key_openHelp"
+ command="cmd_openHelp"/>
+#endif
+ <menuitem id="troubleShooting"
+ accesskey="&helpTroubleshootingInfo.accesskey;"
+ label="&helpTroubleshootingInfo.label;"
+ oncommand="goTroubleshootingPage();"/>
+ <menuitem id="releaseUrl"
+ accesskey="&releaseCmd.accesskey;"
+ label="&releaseCmd.label;"
+ oncommand="goReleaseNotes();"/>
+ <menuitem id="helpSafeMode"
+ accesskey="&helpSafeMode.accesskey;"
+ label="&helpSafeMode.label;"
+ oncommand="safeModeRestart();"/>
+
+ <menuseparator id="updateSeparator"/>
+
+ <menuitem accesskey="&updateCmd.accesskey;" label="&updateCmd.label;"
+ id="checkForUpdates" oncommand="checkForUpdates();"/>
+
+ <menuseparator id="menu_HelpAboutSeparator"/>
+
+ <menuitem class="about"
+ accesskey="&aboutBuildConfigCmd.accesskey;"
+ label="&aboutBuildConfigCmd.label;"
+ id="BuildConfigInfo"
+ oncommand="goAbout('buildconfig');"/>
+ <menuitem accesskey="&aboutCmd.accesskey;"
+ label="&aboutCmd.label;"
+ id="aboutName"
+ oncommand="goAbout();"/>
+ </menupopup>
+ </menu>
+
+ <menupopup id="popupNotificationMenu"
+ oncommand="popupBlockerMenuCommand(event.target);"
+ onpopupshowing="return popupNotificationMenuShowing(event);"
+ onpopuphiding="RemovePopupsItems(this);">
+ <menuitem id="allowPopupsForSite" hidden="true" accesskey="&allowPopups.accesskey;"
+ oncommand="this.notificationbox.allowPopupsForSite(event);"/>
+ <menuitem id="showPopupManager" label="&showPopupManager.label;"
+ accesskey="&showPopupManager.accesskey;"
+ oncommand="toDataManager(hostUrl() + '|permissions|add|popup')"/>
+ <menuitem id="dontShowMessage" label="&dontShowMessage.label;"
+ accesskey="&dontShowMessage.accesskey;"
+ oncommand="disablePopupBlockerNotifications();"/>
+ <menuseparator id="popupNotificationMenuSeparator" hidden="true"/>
+ <!-- Additional items are generated, see popupNotificationMenuShowing() -->
+ </menupopup>
+
+#ifndef XP_MACOSX
+ <toolbar id="toolbar-menubar"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="addrbook-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="compose-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="mail-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="placesToolbar"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+#else
+ <!-- Title bar elements -->
+ <vbox id="titlebar">
+ <hbox id="titlebar-content">
+ <spacer id="titlebar-spacer" flex="1"/>
+ <hbox id="titlebar-buttonbox-container">
+ <hbox id="titlebar-buttonbox"/>
+ </hbox>
+ </hbox>
+ </vbox>
+#endif
+
+ <toolbarbutton id="print-button"
+ type="menu-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ command="cmd_print">
+ <menupopup id="printMenu">
+ <menuitem id="printMenuItemToolbar"
+ label="&printCmd.label;"
+ accesskey="&printCmd.accesskey;"
+ default="true"
+ command="cmd_print"/>
+#ifndef XP_MACOSX
+ <menuitem id="printPreviewMenuItemToolbar"
+ label="&printPreviewCmd.label;"
+ accesskey="&printPreviewCmd.accesskey;"
+ command="cmd_printpreview"/>
+#endif
+ <menuitem id="printSetupToolbar"
+ label="&printSetupCmd.label;"
+ accesskey="&printSetupCmd.accesskey;"
+ command="cmd_printSetup"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbaritem id="throbber-box"
+ class="toolbaritem-noline"
+ title="&throbber.title;"
+ removable="true"
+ align="center">
+ <button id="navigator-throbber"
+ onclick="checkForMiddleClick(this, event);"
+ oncommand="goClickThrobber('app.vendorURL', event);"
+ tooltiptext="&throbber.tooltip2;"/>
+ </toolbaritem>
+
+ <!-- Sync toolbar button
+ <toolbarbutton id="sync-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&syncToolbarButton.label;"
+ oncommand="gSyncUI.handleToolbarButton();"/> -->
+</overlay>
diff --git a/comm/suite/base/content/viewApplyThemeOverlay.js b/comm/suite/base/content/viewApplyThemeOverlay.js
new file mode 100644
index 0000000000..557dc50a3b
--- /dev/null
+++ b/comm/suite/base/content/viewApplyThemeOverlay.js
@@ -0,0 +1,170 @@
+/* -*- 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/. */
+
+const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
+ "resource://gre/modules/LightweightThemeManager.jsm");
+
+var gThemes = [];
+var gApplyThemeBundle;
+var gBackgroundIsActive;
+
+function reloadThemes()
+{
+ AddonManager.getAddonsByTypes(["theme"], function(themes) {
+ gThemes = themes.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+ });
+}
+
+var gAddonListener = {
+ onEnabling: function(val) {},
+ onEnabled: function(val) {},
+ onDisabling: function(val) {},
+ onDisabled: function(val) {},
+ onInstalling: function(val) {},
+ onInstalled: reloadThemes,
+ onUninstalling: function(val) {},
+ onUninstalled: reloadThemes,
+ onOperationCancelled: reloadThemes
+};
+
+function getNewThemes()
+{
+ // get URL for more themes from prefs
+ try {
+ openURL(Services.urlFormatter.formatURLPref("extensions.getMoreThemesURL"));
+ }
+ catch (e) {
+ dump(e);
+ }
+}
+
+function getPersonas()
+{
+ // get URL for more themes from prefs
+ try {
+ openURL(Services.urlFormatter.formatURLPref("extensions.getPersonasURL"));
+ }
+ catch (e) {
+ dump(e);
+ }
+}
+
+function checkTheme(popup)
+{
+ const ID_SUFFIX = "@personas.mozilla.org";
+ gBackgroundIsActive = false;
+
+ var usedThemes = LightweightThemeManager.usedThemes;
+ usedThemes = new Map(usedThemes.map( x => [x.id + ID_SUFFIX, x] ));
+
+ while (popup.lastChild.localName != 'menuseparator')
+ popup.lastChild.remove();
+ gThemes.forEach(function(theme) {
+ var menuitem = document.createElement('menuitem');
+ menuitem.setAttribute("label", theme.name);
+ menuitem.setAttribute("type", "radio");
+ menuitem.setAttribute("name", "themeGroup");
+ if (theme.description)
+ menuitem.setAttribute("tooltiptext", theme.description);
+ if (!theme.userDisabled)
+ menuitem.setAttribute("checked", "true");
+ else if (!(theme.permissions & AddonManager.PERM_CAN_ENABLE))
+ menuitem.setAttribute("disabled", "true");
+ menuitem.theme = theme;
+
+ var persona = usedThemes.get(theme.id);
+ if (persona) {
+ menuitem.persona = persona;
+ if (theme.isActive)
+ gBackgroundIsActive = true;
+ }
+
+ popup.appendChild(menuitem);
+ });
+}
+
+function previewTheme(event)
+{
+ if (!gBackgroundIsActive || !event.target.persona)
+ return;
+
+ switch (event.type) {
+ case "DOMMenuItemActive":
+ LightweightThemeManager.previewTheme(event.target.persona);
+ break;
+ case "DOMMenuItemInactive":
+ LightweightThemeManager.resetPreview();
+ break;
+ }
+}
+
+function restartApp()
+{
+ // Notify all windows that an application quit has been requested.
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+ // Something aborted the quit process.
+ if (cancelQuit.data)
+ return;
+
+ Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
+ const appStartup = Services.startup;
+ appStartup.quit(appStartup.eRestart | appStartup.eAttemptQuit);
+}
+
+function applyTheme(menuitem)
+{
+ if (!menuitem.theme)
+ return;
+
+ menuitem.theme.userDisabled = false;
+ if (!menuitem.theme.isActive) {
+ var promptTitle = gApplyThemeBundle.getString("switchskinstitle");
+ // gBrandBundle: bundle_brand stringbundle from overlayed XUL file
+ var brandName = gBrandBundle.getString("brandShortName");
+ var promptMsg = gApplyThemeBundle.getFormattedString("switchskins", [brandName]);
+ var promptNow = gApplyThemeBundle.getString("switchskinsnow");
+ var promptLater = gApplyThemeBundle.getString("switchskinslater");
+ var check = {value: false};
+ var flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
+ Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING;
+ var pressedVal = Services.prompt.confirmEx(window, promptTitle, promptMsg,
+ flags, promptNow, promptLater,
+ null, null, check);
+ if (pressedVal == 0)
+ restartApp();
+ }
+}
+
+function applyThemeOnLoad()
+{
+ // init globals
+ gApplyThemeBundle = document.getElementById("bundle_viewApplyTheme");
+ AddonManager.addAddonListener(gAddonListener);
+ reloadThemes();
+
+ removeEventListener("load", applyThemeOnLoad, false);
+ addEventListener("unload", applyThemeOnUnload, false);
+ var popup = document.getElementById("menu_viewApplyTheme_Popup");
+ popup.addEventListener("DOMMenuItemActive", previewTheme);
+ popup.addEventListener("DOMMenuItemInactive", previewTheme);
+}
+
+function applyThemeOnUnload()
+{
+ AddonManager.removeAddonListener(gAddonListener);
+ var popup = document.getElementById("menu_viewApplyTheme_Popup");
+ popup.removeEventListener("DOMMenuItemActive", previewTheme);
+ popup.removeEventListener("DOMMenuItemInactive", previewTheme);
+}
+
+addEventListener("load", applyThemeOnLoad, false);
diff --git a/comm/suite/base/content/viewApplyThemeOverlay.xul b/comm/suite/base/content/viewApplyThemeOverlay.xul
new file mode 100644
index 0000000000..0e0097776a
--- /dev/null
+++ b/comm/suite/base/content/viewApplyThemeOverlay.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<!-- 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 overlay SYSTEM "chrome://communicator/locale/viewApplyThemeOverlay.dtd">
+
+<overlay id="viewApplyThemeOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://communicator/content/viewApplyThemeOverlay.js"/>
+
+ <stringbundle id="bundle_viewApplyTheme"
+ src="chrome://communicator/locale/viewApplyThemeOverlay.properties"/>
+
+ <menu id="menu_viewApplyTheme"
+ label="&applyTheme.label;"
+ accesskey="&applyTheme.accesskey;">
+ <menupopup id="menu_viewApplyTheme_Popup"
+ onpopupshowing="checkTheme(this);"
+ oncommand="applyTheme(event.target);">
+ <menuitem label="&getMoreThemesCmd.label;"
+ accesskey="&getMoreThemesCmd.accesskey;"
+ oncommand="getNewThemes();"/>
+ <menuitem label="&getBackgroundsCmd.label;"
+ accesskey="&getBackgroundsCmd.accesskey;"
+ oncommand="getPersonas();"/>
+ <menuseparator/>
+ </menupopup>
+ </menu>
+
+</overlay>
diff --git a/comm/suite/base/content/viewSourceOverlay.js b/comm/suite/base/content/viewSourceOverlay.js
new file mode 100644
index 0000000000..8bcedb2087
--- /dev/null
+++ b/comm/suite/base/content/viewSourceOverlay.js
@@ -0,0 +1,32 @@
+/* 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/. */
+
+// onload make sure we adapt what's needed for partial source
+window.addEventListener("load", onLoadViewSourceOverlay);
+
+function onLoadViewSourceOverlay() {
+ if (/viewPartialSource\.xul$/.test(document.location)) {
+ // disable menu items that don't work since the selection is munged and
+ // the editor doesn't work for MathML
+ document.getElementById('cmd_savePage').setAttribute('disabled', 'true');
+ document.getElementById('cmd_editPage').setAttribute('disabled', 'true');
+ }
+}
+
+// editPage() comes in from editorApplicationOverlay.js
+function ViewSourceEditPage() {
+ editPage(window.content.location.href);
+}
+
+// needed by findUtils.js
+var gFindInstData;
+function getFindInstData()
+{
+ if (!gFindInstData) {
+ gFindInstData = new nsFindInstData();
+ gFindInstData.browser = getBrowser();
+ // defaults for rootSearchWindow and currentSearchWindow are fine here
+ }
+ return gFindInstData;
+}
diff --git a/comm/suite/base/content/viewSourceOverlay.xul b/comm/suite/base/content/viewSourceOverlay.xul
new file mode 100644
index 0000000000..f5ede156f0
--- /dev/null
+++ b/comm/suite/base/content/viewSourceOverlay.xul
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/viewSourceOverlay.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+
+<!DOCTYPE overlay [
+
+<!ENTITY % navDTD SYSTEM "chrome://navigator/locale/navigator.dtd">
+%navDTD;
+
+]>
+
+<overlay id="viewSourceOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://communicator/content/viewSourceOverlay.js"/>
+ <script src="chrome://communicator/content/findUtils.js"/>
+
+ <window id="viewSource">
+ <commandset id="tasksCommands"/>
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newEditor"/>
+ <command id="cmd_editPage" oncommand="ViewSourceEditPage();"/>
+ <command id="cmd_find"
+ oncommand="findInPage(getFindInstData());"/>
+ <command id="cmd_findAgain"
+ oncommand="findAgainInPage(getFindInstData(), false);"/>
+ <command id="cmd_findPrevious"
+ oncommand="findAgainInPage(getFindInstData(), true);"/>
+ <stringbundle id="findBundle"
+ src="chrome://global/locale/finddialog.properties"/>
+ </window>
+
+ <keyset id="viewSourceKeys">
+ <keyset id="tasksKeys"/>
+ <key id="key_newBlankPage"/>
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_editPage" key="&editPageCmd.commandkey;"
+ command="Browser:EditPage" modifiers="accel"/>
+ </keyset>
+
+ <menubar id="viewSource-main-menubar"
+ class="chromeclass-menubar"
+ grippytooltiptext="&menuBar.tooltip;">
+ <menu id="menu_file">
+ <menupopup id="menu_FilePopup">
+ <menu id="menu_New" position="1">
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuitem id="menu_newEditor"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_editPage" insertafter="menu_savePage"
+ key="key_editPage" command="cmd_editPage"
+ label="&editPageCmd.label;"
+ accesskey="&editPageCmd.accesskey;"/>
+ <menuseparator insertbefore="menu_pageSetup"/>
+ </menupopup>
+ </menu>
+
+ <!-- tasks menu filled from tasksOverlay -->
+ <menu id="tasksMenu"/>
+
+ <!-- window menu filled from tasksOverlay -->
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+ </menubar>
+
+</overlay>
diff --git a/comm/suite/base/content/viewZoomOverlay.js b/comm/suite/base/content/viewZoomOverlay.js
new file mode 100644
index 0000000000..d14ef9d67d
--- /dev/null
+++ b/comm/suite/base/content/viewZoomOverlay.js
@@ -0,0 +1,479 @@
+/* -*- 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/. */
+
+// One of the possible values for the mousewheel.* preferences.
+// From nsEventStateManager.cpp.
+const MOUSE_SCROLL_ZOOM = 3;
+
+/**
+ * Controls the "full zoom" setting and its site-specific preferences.
+ */
+var FullZoom = FullZoom || {
+ // Identifies the setting in the content prefs database.
+ name: "browser.content.full-zoom",
+
+ // The global value (if any) for the setting. Asynchronously loaded from the
+ // service when first requested, then updated by the pref change listener as
+ // it changes. If there is no global value, then this should be undefined.
+ globalValue: undefined,
+
+ // browser.zoom.siteSpecific preference cache
+ _siteSpecificPref: undefined,
+
+ // browser.zoom.updateBackgroundTabs preference cache
+ updateBackgroundTabs: undefined,
+
+ get siteSpecific() {
+ return this._siteSpecificPref;
+ },
+
+ //**************************************************************************//
+ // nsISupports
+
+ QueryInterface:
+ XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
+ Ci.nsIObserver,
+ Ci.nsIContentPrefObserver,
+ Ci.nsIContentPrefCallback2,
+ Ci.nsISupportsWeakReference]),
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ init: function FullZoom_init() {
+ // Listen for scrollwheel events so we can save scrollwheel-based changes.
+ window.addEventListener("wheel", this, true);
+
+ // Fetch the initial global value.
+ Services.contentPrefs2.getGlobal(this.name, null, this);
+
+ // Register ourselves with the service so we know when our pref changes.
+ Services.contentPrefs2.addObserverForName(this.name, this);
+
+ this._siteSpecificPref =
+ Services.prefs.getBoolPref("browser.zoom.siteSpecific");
+ this.updateBackgroundTabs =
+ Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");
+ // Listen for changes to the browser.zoom branch so we can enable/disable
+ // updating background tabs and per-site saving and restoring of zoom levels.
+ Services.prefs.addObserver("browser.zoom.", this, true);
+ },
+
+ destroy: function FullZoom_destroy() {
+ Services.prefs.removeObserver("browser.zoom.", this);
+ Services.contentPrefs2.removeObserverForName(this.name, this);
+ window.removeEventListener("wheel", this, true);
+ },
+
+
+ //**************************************************************************//
+ // Event Handlers
+
+ // nsIDOMEventListener
+
+ handleEvent: function FullZoom_handleEvent(event) {
+ switch (event.type) {
+ case "wheel":
+ this._handleMouseScrolled(event);
+ break;
+ }
+ },
+
+ _handleMouseScrolled: function FullZoom_handleMouseScrolled(event) {
+ // Construct the "mousewheel action" pref key corresponding to this event.
+ // Based on nsEventStateManager::WheelPrefs::GetIndexFor.
+ var modifiers = {
+ Alt: "mousewheel.with_alt.action",
+ Control: "mousewheel.with_control.action",
+ Meta: "mousewheel.with_meta.action",
+ Shift: "mousewheel.with_shift.action",
+ OS: "mousewheel.with_win.action"
+ };
+ var pref = [];
+ for (var key in modifiers)
+ if (event.getModifierState(key))
+ pref.push(modifiers[key]);
+ if (pref.length == 1)
+ pref = pref[0];
+ else // Multiple or no modifiers, use default action
+ pref = "mousewheel.default.action";
+
+ // Don't do anything if this isn't a "zoom" scroll event.
+ if (Services.prefs.getIntPref(pref, 0) != MOUSE_SCROLL_ZOOM)
+ return;
+
+ // XXX Lazily cache all the possible action prefs so we don't have to get
+ // them anew from the pref service for every scroll event? We'd have to
+ // make sure to observe them so we can update the cache when they change.
+
+ // We have to call _applySettingToPref in a timeout because we handle
+ // the event before the event state manager has a chance to apply the zoom
+ // during nsEventStateManager::PostHandleEvent.
+ window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
+ },
+
+ // nsIObserver
+
+ observe: function (aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "nsPref:changed":
+ switch (aData) {
+ case "browser.zoom.siteSpecific":
+ this._siteSpecificPref =
+ Services.prefs.getBoolPref("browser.zoom.siteSpecific");
+ break;
+ case "browser.zoom.updateBackgroundTabs":
+ this.updateBackgroundTabs =
+ Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");
+ break;
+ }
+ break;
+ }
+ },
+
+ // nsIContentPrefObserver
+
+ onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
+ if (aGroup == Services.contentPrefs2.extractDomain(getBrowser().currentURI.spec))
+ this._applyPrefToSetting(aValue);
+ else if (aGroup == null) {
+ this.globalValue = this._ensureValid(aValue);
+
+ // If the current page doesn't have a site-specific preference,
+ // then its zoom should be set to the new global preference now that
+ // the global preference has changed.
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ if (zoomValue && !zoomValue.value)
+ this._applyPrefToSetting();
+ }
+ },
+
+ onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
+ if (aGroup == Services.contentPrefs2.extractDomain(getBrowser().currentURI.spec))
+ this._applyPrefToSetting();
+ else if (aGroup == null) {
+ this.globalValue = undefined;
+
+ // If the current page doesn't have a site-specific preference,
+ // then its zoom should be set to the default preference now that
+ // the global preference has changed.
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ if (zoomValue && !zoomValue.value)
+ this._applyPrefToSetting();
+ }
+ },
+
+ // nsIContentPrefCallback2
+
+ handleCompletion: function(aReason) {},
+ handleError: function(aResult) {},
+ handleResult: function(aPref) {
+ this.onContentPrefSet(null, this.name, aPref.value);
+ },
+
+ // location change observer
+
+ /**
+ * Called when the location of a tab changes.
+ * When that happens, we need to update the current zoom level if appropriate.
+ *
+ * @param aURI
+ * A URI object representing the new location.
+ * @param aIsTabSwitch
+ * Whether this location change has happened because of a tab switch.
+ * @param aBrowser
+ * (optional) browser object displaying the document
+ */
+ onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
+ if (!aURI || !this.siteSpecific)
+ return;
+
+ // Avoid the cps roundtrip and apply the default/global pref.
+ if (aURI.spec == "about:blank") {
+ this._applyPrefToSetting(undefined, aBrowser);
+ return;
+ }
+
+ // Image documents should always start at 1, and are not affected by prefs.
+ if (!aIsTabSwitch && aBrowser.contentDocument.mozSyntheticDocument) {
+ ZoomManager.setZoomForBrowser(aBrowser, this._ensureValid(1));
+ return;
+ }
+
+ var loadContext = aBrowser.docShell;
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(aURI.spec, this.name, loadContext);
+ if (zoomValue) {
+ this._applyPrefToSetting(zoomValue.value, aBrowser);
+ } else {
+ Services.contentPrefs2.getByDomainAndName(aURI.spec, this.name, loadContext, {
+ self: this,
+ value: undefined,
+ handleCompletion: function(aReason) {
+ // Check that we're still where we expect to be in case this took a
+ // while. Null check currentURI, since the window may have been
+ // destroyed before we were called.
+ if (aBrowser.currentURI && aURI.equals(aBrowser.currentURI))
+ this.self._applyPrefToSetting(this.value, aBrowser);
+ },
+ handleError: function(aResult) {},
+ handleResult: function(aPref) {
+ this.value = aPref.value;
+ }
+ });
+ }
+ },
+
+ //**************************************************************************//
+ // Setting & Pref Manipulation
+
+ reduce: function FullZoom_reduce() {
+ ZoomManager.reduce();
+ this._applySettingToPref();
+ },
+
+ enlarge: function FullZoom_enlarge() {
+ ZoomManager.enlarge();
+ this._applySettingToPref();
+ },
+
+ zoom: function FullZoom_zoom(aZoomValue) {
+ ZoomManager.zoom = aZoomValue;
+ this._applySettingToPref();
+ },
+
+ reset: function FullZoom_reset() {
+ if (typeof this.globalValue != "undefined")
+ ZoomManager.zoom = this.globalValue;
+ else
+ ZoomManager.zoom = this._ensureValid(1);
+
+ this._removePref();
+ },
+
+ setOther: function setZoomOther() {
+ if (openZoomDialog())
+ this._applySettingToPref();
+ },
+
+ /**
+ * Set the zoom level for the current tab.
+ *
+ * Per nsPresContext::setFullZoom, we can set the zoom to its current value
+ * without significant impact on performance, as the setting is only applied
+ * if it differs from the current setting. In fact getting the zoom and then
+ * checking ourselves if it differs costs more.
+ *
+ * And perhaps we should always set the zoom even if it was more expensive,
+ * since DocumentViewerImpl::SetTextZoom claims that child documents can have
+ * a different text zoom (although it would be unusual), and it implies that
+ * those child text zooms should get updated when the parent zoom gets set,
+ * and perhaps the same is true for full zoom
+ * (although DocumentViewerImpl::SetFullZoom doesn't mention it).
+ *
+ * So when we apply new zoom values to the browser, we simply set the zoom.
+ * We don't check first to see if the new value is the same as the current
+ * one.
+ **/
+ _applyPrefToSetting: function FullZoom_applyPrefToSetting(aValue, aBrowser) {
+ var browser = aBrowser || getBrowser();
+
+ if (!this.siteSpecific || window.gInPrintPreviewMode ||
+ browser.contentDocument.mozSyntheticDocument)
+ return;
+
+ try {
+ if (typeof aValue != "undefined")
+ ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
+ else if (typeof this.globalValue != "undefined")
+ ZoomManager.setZoomForBrowser(browser, this.globalValue);
+ else
+ ZoomManager.setZoomForBrowser(browser, this._ensureValid(1));
+ }
+ catch(ex) {}
+ },
+
+ _applySettingToPref: function FullZoom_applySettingToPref() {
+ if (!this.siteSpecific || window.gInPrintPreviewMode ||
+ content.document.mozSyntheticDocument)
+ return;
+
+ var zoomLevel = ZoomManager.zoom;
+ Services.contentPrefs2.set(getBrowser().currentURI.spec, this.name, zoomLevel, getBrowser().docShell);
+ },
+
+ _removePref: function FullZoom_removePref() {
+ if (!content.document.mozSyntheticDocument)
+ Services.contentPrefs2.removeByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ },
+
+
+ //**************************************************************************//
+ // Utilities
+
+ _ensureValid: function FullZoom_ensureValid(aValue) {
+ if (isNaN(aValue))
+ aValue = 1;
+
+ if (aValue < ZoomManager.MIN)
+ return ZoomManager.MIN;
+
+ if (aValue > ZoomManager.MAX)
+ return ZoomManager.MAX;
+
+ return aValue;
+ }
+};
+
+/***** init and helper functions for viewZoomOverlay.xul *****/
+window.addEventListener("load", registerZoomManager);
+window.addEventListener("unload", unregisterZoomManager);
+
+function registerZoomManager() {
+ FullZoom.init();
+
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomMenu = document.getElementById("menu_zoom");
+ var parentMenu = zoomMenu.parentNode;
+ parentMenu.addEventListener("popupshowing", updateViewMenu);
+
+ // initialize menu from toolkit.zoomManager.zoomValues and assign accesskeys
+ var zoomFactors = ZoomManager.zoomValues;
+ var freeKeys = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
+
+ var insertBefore = document.getElementById("menu_zoomInsertBefore");
+ var popup = insertBefore.parentNode;
+ for (var i = 0; i < zoomFactors.length; ++i) {
+ var thisFactor = Math.round(zoomFactors[i] * 100);
+ var menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("type", "radio");
+ menuItem.setAttribute("name", "zoom");
+
+ var label;
+ var accessKey = "";
+ if (thisFactor == 100) {
+ label = zoomBundle.getString("zoom.100.label");
+ accessKey = zoomBundle.getString("zoom.100.accesskey");
+ menuItem.setAttribute("key", "key_zoomReset");
+ }
+ else if (thisFactor == 200) {
+ label = zoomBundle.getString("zoom.200.label");
+ accessKey = zoomBundle.getString("zoom.200.accesskey");
+ }
+ else if (thisFactor == Math.round(ZoomManager.MIN * 100)) {
+ label = zoomBundle.getString("zoom.min.label")
+ .replace(/%zoom%/, thisFactor);
+ accessKey = zoomBundle.getString("zoom.min.accesskey");
+ }
+ else if (thisFactor == Math.round(ZoomManager.MAX * 100)) {
+ label = zoomBundle.getString("zoom.max.label")
+ .replace(/%zoom%/, thisFactor);
+ accessKey = zoomBundle.getString("zoom.max.accesskey");
+ }
+ else {
+ label = zoomBundle.getString("zoom.value.label")
+ .replace(/%zoom%/, thisFactor);
+ for (var j = 0; j < label.length; ++j) {
+ var testKey = label[j];
+ var indexKey = freeKeys.indexOf(testKey);
+ if (indexKey >= 0) {
+ accessKey = testKey;
+ freeKeys.splice(indexKey, 1);
+ break;
+ }
+ }
+ }
+
+ menuItem.setAttribute("label", label);
+ if (accessKey)
+ menuItem.setAttribute("accesskey", accessKey);
+ menuItem.setAttribute("value", thisFactor);
+ popup.insertBefore(menuItem, insertBefore);
+ }
+}
+
+function unregisterZoomManager() {
+ FullZoom.destroy();
+}
+
+function updateViewMenu() {
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomMenu = document.getElementById("menu_zoom");
+ var zoomType = ZoomManager.useFullZoom ? "fullZoom" : "textZoom";
+ var menuLabel = zoomBundle.getString(zoomType + ".label")
+ .replace(/%zoom%/, Math.round(ZoomManager.zoom * 100));
+ var menuKey = zoomBundle.getString(zoomType + ".accesskey");
+ zoomMenu.setAttribute("label", menuLabel);
+ zoomMenu.setAttribute("accesskey", menuKey);
+}
+
+function updateZoomMenu() {
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomOther = document.getElementById("menu_zoomOther");
+ var label = zoomBundle.getString("zoom.other.label");
+ var accesskey = zoomBundle.getString("zoom.other.accesskey");
+ var factorOther = zoomOther.getAttribute("value") ||
+ Math.round(ZoomManager.MAX * 100);
+ zoomOther.setAttribute("label", label.replace(/%zoom%/, factorOther));
+ zoomOther.setAttribute("accesskey", accesskey);
+ zoomOther.setAttribute("value", factorOther);
+
+ var popup = document.getElementById("menu_zoomPopup");
+ var item = popup.lastChild;
+ while (item) {
+ if (item.getAttribute("name") == "zoom") {
+ if (item.getAttribute("value") == Math.round(ZoomManager.zoom * 100))
+ item.setAttribute("checked","true");
+ else
+ item.removeAttribute("checked");
+ }
+ item = item.previousSibling;
+ }
+}
+
+function openZoomDialog() {
+ var zoomOther = document.getElementById("menu_zoomOther");
+ // open dialog and ask for new value
+ var o = {value: zoomOther.getAttribute("value"),
+ zoomMin: ZoomManager.MIN * 100,
+ zoomMax: ZoomManager.MAX * 100};
+ window.openDialog("chrome://communicator/content/askViewZoom.xul",
+ "", "chrome,modal,centerscreen", o);
+ if (o.zoomOK) {
+ zoomOther.setAttribute("value", o.value);
+ ZoomManager.zoom = o.value / 100;
+ }
+ return o.zoomOK;
+}
+
+function zoomEnlarge() {
+ FullZoom.enlarge();
+ updateZoomStatus();
+}
+
+function zoomReduce() {
+ FullZoom.reduce();
+ updateZoomStatus();
+}
+
+function zoomReset() {
+ FullZoom.reset();
+ updateZoomStatus();
+}
+
+function zoomSetOther() {
+ FullZoom.setOther();
+ updateZoomStatus();
+}
+
+function zoomToggle() {
+ ZoomManager.toggleZoom();
+ updateZoomStatus();
+}
+
+function zoomSet(aValue) {
+ FullZoom.zoom(aValue)
+ updateZoomStatus();
+}
diff --git a/comm/suite/base/content/viewZoomOverlay.xul b/comm/suite/base/content/viewZoomOverlay.xul
new file mode 100644
index 0000000000..a22c853e3e
--- /dev/null
+++ b/comm/suite/base/content/viewZoomOverlay.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+
+<!-- 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 overlay SYSTEM "chrome://communicator/locale/viewZoomOverlay.dtd">
+
+<overlay id="viewZoomOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://global/content/viewZoomOverlay.js"/>
+ <script src="chrome://communicator/content/viewZoomOverlay.js"/>
+
+ <stringbundle id="bundle_viewZoom" src="chrome://communicator/locale/viewZoomOverlay.properties"/>
+
+ <keyset id="viewZoomKeys">
+ <key id="key_zoomReduce" key="&zoomReduceCmd.commandkey;" command="cmd_zoomReduce" modifiers="accel"/>
+ <key id="key_zoomEnlarge" key="&zoomEnlargeCmd.commandkey;" command="cmd_zoomEnlarge" modifiers="accel"/>
+ <key key="&zoomEnlargeCmd.commandkey;" command="cmd_zoomEnlarge" modifiers="accel,shift"/>
+ <key key="&zoomEnlargeCmd.commandkey2;" command="cmd_zoomEnlarge" modifiers="accel"/>
+ <key id="key_zoomReset" key="&zoomResetCmd.commandkey;" command="cmd_zoomReset" modifiers="accel"/>
+ </keyset>
+
+ <commandset id="viewZoomCommands">
+ <command id="cmd_zoomReduce" oncommand="zoomReduce();"/>
+ <command id="cmd_zoomEnlarge" oncommand="zoomEnlarge();"/>
+ <command id="cmd_zoomReset" oncommand="zoomReset();"/>
+ <command id="cmd_zoomOther" oncommand="zoomSetOther();"/>
+ <command id="cmd_fullZoomToggle" oncommand="zoomToggle();"/>
+ </commandset>
+
+ <menu id="menu_zoom">
+ <menupopup id="menu_zoomPopup" onpopupshowing="updateZoomMenu();" oncommand="zoomSet(event.target.value / 100);">
+ <menuitem id="menu_zoomReduce"
+ key="key_zoomReduce"
+ label="&zoomReduceCmd.label;"
+ accesskey="&zoomReduceCmd.accesskey;"
+ command="cmd_zoomReduce"/>
+ <menuitem id="menu_zoomEnlarge"
+ key="key_zoomEnlarge"
+ label="&zoomEnlargeCmd.label;"
+ accesskey="&zoomEnlargeCmd.accesskey;"
+ command="cmd_zoomEnlarge"/>
+ <menuseparator/>
+ <menuseparator id="menu_zoomInsertBefore"/>
+ <menuitem id="menu_zoomOther"
+ type="radio"
+ name="zoom"
+ command="cmd_zoomOther"/>
+ </menupopup>
+ </menu>
+
+</overlay>
diff --git a/comm/suite/base/jar.mn b/comm/suite/base/jar.mn
new file mode 100644
index 0000000000..910f8e2251
--- /dev/null
+++ b/comm/suite/base/jar.mn
@@ -0,0 +1,96 @@
+#filter substitution
+
+# 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/.comm.jar:
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+% override chrome://browser/content/utilityOverlay.js chrome://communicator/content/utilityOverlay.js
+% override chrome://global/content/license.html chrome://communicator/content/license.html
+% override chrome://global/content/netError.xhtml chrome://communicator/content/certError.xhtml
+% overlay chrome://global/content/viewSource.xul chrome://communicator/content/viewSourceOverlay.xul
+% overlay chrome://global/content/viewPartialSource.xul chrome://communicator/content/viewSourceOverlay.xul
+% overlay chrome://editor/content/EdAdvancedEdit.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdImageProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublish.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublishProgress.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublishSettings.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdLinkProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdTableProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://pippki/content/certManager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/certViewer.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/changepassword.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/device_manager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/choosetoken.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/clientauthask.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/crlImportDialog.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/deletecert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editcacert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editemailcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editsslcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/escrowWarn.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/getp12password.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/setp12password.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/pref-crlupdate.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/serverCrlNextupdate.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/crlManager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/resetpassword.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/downloadcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://messenger/content/addressbook/pref-editdirectories.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/addressbook/pref-directory-add.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/AccountManager.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/FilterEditor.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/junkMailInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/msgSelectOfflineFolders.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/subscribe.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/mailViewList.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/mailViewSetup.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger-smime/content/msgCompSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger-smime/content/msgReadSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% style about:addons chrome://communicator/content/extensionsOverlay.css
+% style chrome://mozapps/content/extensions/extensions.xul chrome://communicator/content/extensionsOverlay.css
+ content/communicator/about.js (content/about.js)
+ content/communicator/about.xhtml (content/about.xhtml)
+ content/communicator/aboutLife.xhtml (content/aboutLife.xhtml)
+ content/communicator/aboutPrivateBrowsing.css (content/aboutPrivateBrowsing.css)
+ content/communicator/aboutPrivateBrowsing.js (content/aboutPrivateBrowsing.js)
+ content/communicator/aboutPrivateBrowsing.xul (content/aboutPrivateBrowsing.xul)
+ content/communicator/askViewZoom.xul (content/askViewZoom.xul)
+ content/communicator/askViewZoom.js (content/askViewZoom.js)
+ content/communicator/blockedSite.js (content/blockedSite.js)
+ content/communicator/blockedSite.xhtml (content/blockedSite.xhtml)
+ content/communicator/certError.css (content/certError.css)
+ content/communicator/certError.js (content/certError.js)
+ content/communicator/certError.xhtml (content/certError.xhtml)
+ content/communicator/certError.xml (content/certError.xml)
+ content/communicator/charsetOverlay.xul (content/charsetOverlay.xul)
+* content/communicator/communicator.css (content/communicator.css)
+ content/communicator/contentAreaClick.js (content/contentAreaClick.js)
+ content/communicator/contentAreaContextOverlay.xul (content/contentAreaContextOverlay.xul)
+ content/communicator/defaultClientDialog.js (content/defaultClientDialog.js)
+ content/communicator/defaultClientDialog.xul (content/defaultClientDialog.xul)
+ content/communicator/extensionsOverlay.css (content/extensionsOverlay.css)
+ content/communicator/findUtils.js (content/findUtils.js)
+ content/communicator/fullscreen-video.xhtml (content/fullscreen-video.xhtml)
+ content/communicator/gopherAddon.xhtml (content/gopherAddon.xhtml)
+ content/communicator/helpEditorOverlay.xul (content/helpEditorOverlay.xul)
+ content/communicator/helpMessengerOverlay.xul (content/helpMessengerOverlay.xul)
+ content/communicator/helpSecurityOverlay.xul (content/helpSecurityOverlay.xul)
+# the following file is a suite-specific override of the generic license.html, using suite/common/app-license.html as input:
+* content/communicator/license.html (/toolkit/content/license.html)
+ content/communicator/nsContextMenu.js (content/nsContextMenu.js)
+ content/communicator/openLocation.js (content/openLocation.js)
+ content/communicator/openLocation.xul (content/openLocation.xul)
+ content/communicator/safeMode.js (content/safeMode.js)
+ content/communicator/safeMode.xul (content/safeMode.xul)
+ content/communicator/tasksOverlay.js (content/tasksOverlay.js)
+* content/communicator/tasksOverlay.xul (content/tasksOverlay.xul)
+ content/communicator/utilityOverlay.js (content/utilityOverlay.js)
+* content/communicator/utilityOverlay.xul (content/utilityOverlay.xul)
+ content/communicator/viewApplyThemeOverlay.xul (content/viewApplyThemeOverlay.xul)
+ content/communicator/viewApplyThemeOverlay.js (content/viewApplyThemeOverlay.js)
+ content/communicator/viewSourceOverlay.js (content/viewSourceOverlay.js)
+ content/communicator/viewSourceOverlay.xul (content/viewSourceOverlay.xul)
+ content/communicator/viewZoomOverlay.xul (content/viewZoomOverlay.xul)
+ content/communicator/viewZoomOverlay.js (content/viewZoomOverlay.js)
diff --git a/comm/suite/base/moz.build b/comm/suite/base/moz.build
new file mode 100644
index 0000000000..5ca66f7aa6
--- /dev/null
+++ b/comm/suite/base/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+# DEFINES for preprocessing
+
+# Use suite/base/content/overrides/app-license.html as input when generating
+# chrome://content/communicator/license.html to override
+# chrome://global/content/license.html (about:license)
+DEFINES["APP_LICENSE_BLOCK"] = "%s/content/overrides/app-license.html" % SRCDIR
+
+for var in ("MOZ_APP_NAME", "MOZ_MACBUNDLE_NAME"):
+ DEFINES[var] = CONFIG[var]