mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 06:45:42 +00:00
cbb0d57034
Instead of relying on untrusted click/auxclick events anywhere instantiating the actor, and then having to look for links, after this patch we'll only instantiate the actor for actual link clicks. This patch moves to using a chrome-only command event (with type `chromelinkclick`) dispatched from the link click post-visitor to accomplish that. In future we should probably move both this and the middle-click-to-paste handling into DOM code (or, for the latter, remove it) but this is a less invasive solution. This also moves the middle-click-to-paste handling into its own listener. It needs to listen to page events in general (not just links) but is disabled everywhere by default, so registering an actor for everyone doesn't seem like a good trade-off. To avoid duplicating all the logic (we do need to avoid doing middle-click navigation based on the clipboard when clicking on links!), as well as keeping patch size down, the actual control flow goes through the click handler actor still. Differential Revision: https://phabricator.services.mozilla.com/D134011
200 lines
6.1 KiB
JavaScript
200 lines
6.1 KiB
JavaScript
/* -*- 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/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["ClickHandlerChild", "MiddleMousePasteHandlerChild"];
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
);
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"WebNavigationFrames",
|
|
"resource://gre/modules/WebNavigationFrames.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"E10SUtils",
|
|
"resource://gre/modules/E10SUtils.jsm"
|
|
);
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"BrowserUtils",
|
|
"resource://gre/modules/BrowserUtils.jsm"
|
|
);
|
|
|
|
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
|
|
);
|
|
|
|
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] = BrowserUtils.hrefAndLinkNodeForClickEvent(
|
|
event
|
|
);
|
|
|
|
let csp = ownerDoc.csp;
|
|
if (csp) {
|
|
csp = E10SUtils.serializeCSP(csp);
|
|
}
|
|
|
|
let referrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance(
|
|
Ci.nsIReferrerInfo
|
|
);
|
|
if (node) {
|
|
referrerInfo.initWithElement(node);
|
|
} else {
|
|
referrerInfo.initWithDocument(ownerDoc);
|
|
}
|
|
referrerInfo = E10SUtils.serializeReferrerInfo(referrerInfo);
|
|
let frameID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
|
|
|
|
let json = {
|
|
button: event.button,
|
|
shiftKey: event.shiftKey,
|
|
ctrlKey: event.ctrlKey,
|
|
metaKey: event.metaKey,
|
|
altKey: event.altKey,
|
|
href: null,
|
|
title: null,
|
|
frameID,
|
|
triggeringPrincipal: principal,
|
|
csp,
|
|
referrerInfo,
|
|
originAttributes: principal ? principal.originAttributes : {},
|
|
isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(
|
|
ownerDoc.defaultView
|
|
),
|
|
};
|
|
|
|
if (href && !isFromMiddleMousePasteHandler) {
|
|
try {
|
|
Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(
|
|
principal,
|
|
href
|
|
);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!event.isTrusted &&
|
|
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");
|
|
}
|
|
|
|
json.originPrincipal = ownerDoc.nodePrincipal;
|
|
json.originStoragePrincipal = ownerDoc.effectiveStoragePrincipal;
|
|
json.triggeringPrincipal = ownerDoc.nodePrincipal;
|
|
|
|
// 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.
|
|
if (event.button === 1) {
|
|
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);
|
|
}
|
|
}
|
|
}
|