diff options
Diffstat (limited to 'browser/actors/ClickHandlerChild.sys.mjs')
-rw-r--r-- | browser/actors/ClickHandlerChild.sys.mjs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/browser/actors/ClickHandlerChild.sys.mjs b/browser/actors/ClickHandlerChild.sys.mjs new file mode 100644 index 0000000000..c08b599856 --- /dev/null +++ b/browser/actors/ClickHandlerChild.sys.mjs @@ -0,0 +1,177 @@ +/* -*- 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); + } + } +} |