summaryrefslogtreecommitdiffstats
path: root/browser/actors/ClickHandlerChild.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/actors/ClickHandlerChild.sys.mjs')
-rw-r--r--browser/actors/ClickHandlerChild.sys.mjs174
1 files changed, 174 insertions, 0 deletions
diff --git a/browser/actors/ClickHandlerChild.sys.mjs b/browser/actors/ClickHandlerChild.sys.mjs
new file mode 100644
index 0000000000..2fa0a87df4
--- /dev/null
+++ b/browser/actors/ClickHandlerChild.sys.mjs
@@ -0,0 +1,174 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
+ E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
+});
+
+export class MiddleMousePasteHandlerChild extends JSWindowActorChild {
+ handleEvent(clickEvent) {
+ if (
+ clickEvent.defaultPrevented ||
+ clickEvent.button != 1 ||
+ MiddleMousePasteHandlerChild.autoscrollEnabled
+ ) {
+ return;
+ }
+ this.manager
+ .getActor("ClickHandler")
+ .handleClickEvent(
+ clickEvent,
+ /* is from middle mouse paste handler */ true
+ );
+ }
+
+ onProcessedClick(data) {
+ this.sendAsyncMessage("MiddleClickPaste", data);
+ }
+}
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ MiddleMousePasteHandlerChild,
+ "autoscrollEnabled",
+ "general.autoScroll",
+ true
+);
+
+export class ClickHandlerChild extends JSWindowActorChild {
+ handleEvent(wrapperEvent) {
+ this.handleClickEvent(wrapperEvent.sourceEvent);
+ }
+
+ handleClickEvent(event, isFromMiddleMousePasteHandler = false) {
+ if (event.defaultPrevented || event.button == 2) {
+ return;
+ }
+ // Don't do anything on editable things, we shouldn't open links in
+ // contenteditables, and editor needs to possibly handle middlemouse paste
+ let composedTarget = event.composedTarget;
+ if (
+ composedTarget.isContentEditable ||
+ (composedTarget.ownerDocument &&
+ composedTarget.ownerDocument.designMode == "on") ||
+ ChromeUtils.getClassName(composedTarget) == "HTMLInputElement" ||
+ ChromeUtils.getClassName(composedTarget) == "HTMLTextAreaElement"
+ ) {
+ return;
+ }
+
+ let originalTarget = event.originalTarget;
+ let ownerDoc = originalTarget.ownerDocument;
+ if (!ownerDoc) {
+ return;
+ }
+
+ // Handle click events from about pages
+ if (event.button == 0) {
+ if (ownerDoc.documentURI.startsWith("about:blocked")) {
+ return;
+ }
+ }
+
+ // For untrusted events, require a valid transient user gesture activation.
+ if (!event.isTrusted && !ownerDoc.hasValidTransientUserGestureActivation) {
+ return;
+ }
+
+ let [href, node, principal] =
+ lazy.BrowserUtils.hrefAndLinkNodeForClickEvent(event);
+
+ let csp = ownerDoc.csp;
+ if (csp) {
+ csp = lazy.E10SUtils.serializeCSP(csp);
+ }
+
+ let referrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance(
+ Ci.nsIReferrerInfo
+ );
+ if (node) {
+ referrerInfo.initWithElement(node);
+ } else {
+ referrerInfo.initWithDocument(ownerDoc);
+ }
+ referrerInfo = lazy.E10SUtils.serializeReferrerInfo(referrerInfo);
+
+ let json = {
+ button: event.button,
+ shiftKey: event.shiftKey,
+ ctrlKey: event.ctrlKey,
+ metaKey: event.metaKey,
+ altKey: event.altKey,
+ href: null,
+ title: null,
+ csp,
+ referrerInfo,
+ };
+
+ if (href && !isFromMiddleMousePasteHandler) {
+ try {
+ Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
+ principal,
+ href
+ );
+ } catch (e) {
+ return;
+ }
+
+ if (
+ !event.isTrusted &&
+ lazy.BrowserUtils.whereToOpenLink(event) != "current"
+ ) {
+ // If we'll open the link, we want to consume the user gesture
+ // activation to ensure that we don't allow multiple links to open
+ // from one user gesture.
+ // Avoid doing so for links opened in the current tab, which get
+ // handled later, by gecko, as otherwise its popup blocker will stop
+ // the link from opening.
+ // We will do the same check (whereToOpenLink) again in the parent and
+ // avoid handling the click for such links... but we still need the
+ // click information in the parent because otherwise places link
+ // tracking breaks. (bug 1742894 tracks improving this.)
+ ownerDoc.consumeTransientUserGestureActivation();
+ // We don't care about the return value because we already checked that
+ // hasValidTransientUserGestureActivation was true earlier in this
+ // function.
+ }
+
+ json.href = href;
+ if (node) {
+ json.title = node.getAttribute("title");
+ }
+
+ if (
+ (ownerDoc.URL === "about:newtab" || ownerDoc.URL === "about:home") &&
+ node.dataset.isSponsoredLink === "true"
+ ) {
+ json.globalHistoryOptions = { triggeringSponsoredURL: href };
+ }
+
+ // If a link element is clicked with middle button, user wants to open
+ // the link somewhere rather than pasting clipboard content. Therefore,
+ // when it's clicked with middle button, we should prevent multiple
+ // actions here to avoid leaking clipboard content unexpectedly.
+ // Note that whether the link will work actually or not does not matter
+ // because in this case, user does not intent to paste clipboard content.
+ // We also need to do this to prevent multiple tabs opening if there are
+ // nested link elements.
+ event.preventMultipleActions();
+
+ this.sendAsyncMessage("Content:Click", json);
+ }
+
+ // This might be middle mouse navigation, in which case pass this back:
+ if (!href && event.button == 1 && isFromMiddleMousePasteHandler) {
+ this.manager.getActor("MiddleMousePasteHandler").onProcessedClick(json);
+ }
+ }
+}