summaryrefslogtreecommitdiffstats
path: root/comm/mail/base/content/contentAreaClick.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/mail/base/content/contentAreaClick.js206
1 files changed, 206 insertions, 0 deletions
diff --git a/comm/mail/base/content/contentAreaClick.js b/comm/mail/base/content/contentAreaClick.js
new file mode 100644
index 0000000000..647c4e7551
--- /dev/null
+++ b/comm/mail/base/content/contentAreaClick.js
@@ -0,0 +1,206 @@
+/**
+ * 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/. */
+
+/* import-globals-from ../../../../toolkit/content/contentAreaUtils.js */
+/* import-globals-from utilityOverlay.js */
+
+/* globals getMessagePaneBrowser */ // From aboutMessage.js
+
+var { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+XPCOMUtils.defineLazyModuleGetters(this, {
+ PhishingDetector: "resource:///modules/PhishingDetector.jsm",
+});
+XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "alternativeAddonSearchUrl",
+ "extensions.alternativeAddonSearch.url"
+);
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "canonicalAddonServerUrl",
+ "extensions.canonicalAddonServer.url"
+);
+/**
+ * Extract the href from the link click event.
+ * We look for HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement,
+ * HTMLInputElement.form.action, and nested anchor tags.
+ * If the clicked element was a HTMLInputElement or HTMLButtonElement
+ * we return the form action.
+ *
+ * @returns [href, linkText] the url and the text for the link being clicked.
+ */
+function hRefForClickEvent(aEvent, aDontCheckInputElement) {
+ let target =
+ aEvent.type == "command"
+ ? document.commandDispatcher.focusedElement
+ : aEvent.target;
+
+ if (
+ HTMLImageElement.isInstance(target) &&
+ target.hasAttribute("overflowing")
+ ) {
+ // Click on zoomed image.
+ return [null, null];
+ }
+
+ let href = null;
+ let linkText = null;
+ if (
+ HTMLAnchorElement.isInstance(target) ||
+ HTMLAreaElement.isInstance(target) ||
+ HTMLLinkElement.isInstance(target)
+ ) {
+ if (target.hasAttribute("href")) {
+ href = target.href;
+ linkText = gatherTextUnder(target);
+ }
+ } else if (
+ !aDontCheckInputElement &&
+ (HTMLInputElement.isInstance(target) ||
+ HTMLButtonElement.isInstance(target))
+ ) {
+ if (target.form && target.form.action) {
+ href = target.form.action;
+ }
+ } else {
+ // We may be nested inside of a link node.
+ let linkNode = aEvent.target;
+ while (linkNode && !HTMLAnchorElement.isInstance(linkNode)) {
+ linkNode = linkNode.parentNode;
+ }
+
+ if (linkNode) {
+ href = linkNode.href;
+ linkText = gatherTextUnder(linkNode);
+ }
+ }
+ return [href, linkText];
+}
+
+/**
+ * Check whether the click target's or its ancestor's href
+ * points to an anchor on the page.
+ *
+ * @param HTMLElement aTargetNode - the element node.
+ * @returns - true if link pointing to anchor.
+ */
+function isLinkToAnchorOnPage(aTargetNode) {
+ let url = aTargetNode.ownerDocument.URL;
+ if (!url.startsWith("http")) {
+ return false;
+ }
+
+ let linkNode = aTargetNode;
+ while (linkNode && !HTMLAnchorElement.isInstance(linkNode)) {
+ linkNode = linkNode.parentNode;
+ }
+
+ // It's not a link with an anchor.
+ if (!linkNode || !linkNode.href || !linkNode.hash) {
+ return false;
+ }
+
+ // The link's href must match the document URL.
+ if (makeURI(linkNode.href).specIgnoringRef != makeURI(url).specIgnoringRef) {
+ return false;
+ }
+
+ return true;
+}
+
+// Called whenever the user clicks in the content area,
+// should always return true for click to go through.
+function contentAreaClick(aEvent) {
+ let target = aEvent.target;
+ if (target.localName == "browser") {
+ // This is a remote browser. Nothing useful can happen in this process.
+ return true;
+ }
+
+ // If we've loaded a web page url, and the element's or its ancestor's href
+ // points to an anchor on the page, let the click go through.
+ // Otherwise fall through and open externally.
+ if (isLinkToAnchorOnPage(target)) {
+ return true;
+ }
+
+ let [href, linkText] = hRefForClickEvent(aEvent);
+
+ if (!href && !aEvent.button) {
+ // Is this an image that we might want to scale?
+
+ if (HTMLImageElement.isInstance(target)) {
+ // Make sure it loaded successfully. No action if not or a broken link.
+ var req = target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (!req || req.imageStatus & Ci.imgIRequest.STATUS_ERROR) {
+ return false;
+ }
+
+ // Is it an image?
+ if (target.localName == "img" && target.hasAttribute("overflowing")) {
+ if (target.hasAttribute("shrinktofit")) {
+ // Currently shrunk to fit, so unshrink it.
+ target.removeAttribute("shrinktofit");
+ } else {
+ // User wants to shrink now.
+ target.setAttribute("shrinktofit", true);
+ }
+
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if (!href || aEvent.button == 2) {
+ return true;
+ }
+
+ // We want all about, http and https links in the message pane to be loaded
+ // externally in a browser, therefore we need to detect that here and redirect
+ // as necessary.
+ let uri = makeURI(href);
+ if (
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .isExposedProtocol(uri.scheme) &&
+ !uri.schemeIs("http") &&
+ !uri.schemeIs("https")
+ ) {
+ return true;
+ }
+
+ // Add-on names in the Add-On Manager are links, but we don't want to do
+ // anything with them.
+ if (uri.schemeIs("addons")) {
+ return true;
+ }
+
+ // Now we're here, we know this should be loaded in an external browser, so
+ // prevent the default action so we don't try and load it here.
+ aEvent.preventDefault();
+
+ // Let the phishing detector check the link.
+ let urlPhishCheckResult = PhishingDetector.warnOnSuspiciousLinkClick(
+ window,
+ href,
+ linkText
+ );
+ if (urlPhishCheckResult === 1) {
+ return false; // Block request
+ }
+
+ if (urlPhishCheckResult === 0) {
+ // Use linkText instead.
+ openLinkExternally(linkText);
+ return true;
+ }
+
+ openLinkExternally(href);
+ return true;
+}