summaryrefslogtreecommitdiffstats
path: root/browser/actors/ClickHandlerParent.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/actors/ClickHandlerParent.sys.mjs')
-rw-r--r--browser/actors/ClickHandlerParent.sys.mjs160
1 files changed, 160 insertions, 0 deletions
diff --git a/browser/actors/ClickHandlerParent.sys.mjs b/browser/actors/ClickHandlerParent.sys.mjs
new file mode 100644
index 0000000000..fb0484ff18
--- /dev/null
+++ b/browser/actors/ClickHandlerParent.sys.mjs
@@ -0,0 +1,160 @@
+/* -*- mode: js; 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
+ PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+});
+ChromeUtils.defineModuleGetter(
+ lazy,
+ "WebNavigationFrames",
+ "resource://gre/modules/WebNavigationFrames.jsm"
+);
+
+let gContentClickListeners = new Set();
+
+// Fill in fields which are not sent by the content process for the click event
+// based on known data in the parent process.
+function fillInClickEvent(actor, data) {
+ const wgp = actor.manager;
+ data.frameID = lazy.WebNavigationFrames.getFrameId(wgp.browsingContext);
+ data.triggeringPrincipal = wgp.documentPrincipal;
+ data.originPrincipal = wgp.documentPrincipal;
+ data.originStoragePrincipal = wgp.documentStoragePrincipal;
+ data.originAttributes = wgp.documentPrincipal?.originAttributes ?? {};
+ data.isContentWindowPrivate = wgp.browsingContext.usePrivateBrowsing;
+}
+
+export class MiddleMousePasteHandlerParent extends JSWindowActorParent {
+ receiveMessage(message) {
+ if (message.name == "MiddleClickPaste") {
+ // This is heavily based on contentAreaClick from browser.js (Bug 903016)
+ // The data is set up in a way to look like an Event.
+ let browser = this.manager.browsingContext.top.embedderElement;
+ if (!browser) {
+ // Can be null if the tab disappeared by the time we got the message.
+ // Just bail.
+ return;
+ }
+ fillInClickEvent(this, message.data);
+ browser.ownerGlobal.middleMousePaste(message.data);
+ }
+ }
+}
+
+export class ClickHandlerParent extends JSWindowActorParent {
+ static addContentClickListener(listener) {
+ gContentClickListeners.add(listener);
+ }
+
+ static removeContentClickListener(listener) {
+ gContentClickListeners.delete(listener);
+ }
+
+ receiveMessage(message) {
+ switch (message.name) {
+ case "Content:Click":
+ fillInClickEvent(this, message.data);
+ this.contentAreaClick(message.data);
+ this.notifyClickListeners(message.data);
+ break;
+ }
+ }
+
+ /**
+ * Handles clicks in the content area.
+ *
+ * @param data {Object} object that looks like an Event
+ * @param browser {Element<browser>}
+ */
+ contentAreaClick(data) {
+ // This is heavily based on contentAreaClick from browser.js (Bug 903016)
+ // The data is set up in a way to look like an Event.
+ let browser = this.manager.browsingContext.top.embedderElement;
+ if (!browser) {
+ // Can be null if the tab disappeared by the time we got the message.
+ // Just bail.
+ return;
+ }
+ let window = browser.ownerGlobal;
+
+ // If the browser is not in a place where we can open links, bail out.
+ // This can happen in osx sheets, dialogs, etc. that are not browser
+ // windows. Specifically the payments UI is in an osx sheet.
+ if (window.openLinkIn === undefined) {
+ return;
+ }
+
+ // 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 {
+ if (!lazy.PrivateBrowsingUtils.isWindowPrivate(window)) {
+ lazy.PlacesUIUtils.markPageAsFollowedLink(data.href);
+ }
+ } catch (ex) {
+ /* Skip invalid URIs. */
+ }
+
+ // This part is based on handleLinkClick.
+ var where = window.whereToOpenLink(data);
+ if (where == "current") {
+ return;
+ }
+
+ // Todo(903022): code for where == save
+
+ let params = {
+ charset: browser.characterSet,
+ referrerInfo: lazy.E10SUtils.deserializeReferrerInfo(data.referrerInfo),
+ isContentWindowPrivate: data.isContentWindowPrivate,
+ originPrincipal: data.originPrincipal,
+ originStoragePrincipal: data.originStoragePrincipal,
+ triggeringPrincipal: data.triggeringPrincipal,
+ csp: data.csp ? lazy.E10SUtils.deserializeCSP(data.csp) : null,
+ frameID: data.frameID,
+ openerBrowser: browser,
+ // The child ensures that untrusted events have a valid user activation.
+ hasValidUserGestureActivation: true,
+ triggeringRemoteType: this.manager.domProcess?.remoteType,
+ };
+
+ if (data.globalHistoryOptions) {
+ params.globalHistoryOptions = data.globalHistoryOptions;
+ } else {
+ params.globalHistoryOptions = {
+ triggeringSponsoredURL: browser.getAttribute("triggeringSponsoredURL"),
+ triggeringSponsoredURLVisitTimeMS: browser.getAttribute(
+ "triggeringSponsoredURLVisitTimeMS"
+ ),
+ };
+ }
+
+ // The new tab/window must use the same userContextId.
+ if (data.originAttributes.userContextId) {
+ params.userContextId = data.originAttributes.userContextId;
+ }
+
+ params.allowInheritPrincipal = true;
+
+ window.openLinkIn(data.href, where, params);
+ }
+
+ notifyClickListeners(data) {
+ for (let listener of gContentClickListeners) {
+ try {
+ let browser = this.browsingContext.top.embedderElement;
+
+ listener.onContentClick(browser, data);
+ } catch (ex) {
+ console.error(ex);
+ }
+ }
+ }
+}