ClickHandlerParent.sys.mjs (5730B)
1 /* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 const lazy = {}; 7 8 ChromeUtils.defineESModuleGetters(lazy, { 9 BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", 10 E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs", 11 PlacesUIUtils: "moz-src:///browser/components/places/PlacesUIUtils.sys.mjs", 12 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", 13 WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs", 14 }); 15 16 let gContentClickListeners = new Set(); 17 18 // Fill in fields which are not sent by the content process for the click event 19 // based on known data in the parent process. 20 function fillInClickEvent(actor, data) { 21 const wgp = actor.manager; 22 data.frameID = lazy.WebNavigationFrames.getFrameId(wgp.browsingContext); 23 data.triggeringPrincipal = wgp.documentPrincipal; 24 data.originPrincipal = wgp.documentPrincipal; 25 data.originStoragePrincipal = wgp.documentStoragePrincipal; 26 data.originAttributes = wgp.documentPrincipal?.originAttributes ?? {}; 27 data.isContentWindowPrivate = wgp.browsingContext.usePrivateBrowsing; 28 } 29 30 export class MiddleMousePasteHandlerParent extends JSWindowActorParent { 31 receiveMessage(message) { 32 if (message.name == "MiddleClickPaste") { 33 // This is heavily based on contentAreaClick from browser.js (Bug 903016) 34 // The data is set up in a way to look like an Event. 35 let browser = this.manager.browsingContext.top.embedderElement; 36 if (!browser) { 37 // Can be null if the tab disappeared by the time we got the message. 38 // Just bail. 39 return; 40 } 41 fillInClickEvent(this, message.data); 42 browser.ownerGlobal.middleMousePaste(message.data); 43 } 44 } 45 } 46 47 export class ClickHandlerParent extends JSWindowActorParent { 48 static addContentClickListener(listener) { 49 gContentClickListeners.add(listener); 50 } 51 52 static removeContentClickListener(listener) { 53 gContentClickListeners.delete(listener); 54 } 55 56 receiveMessage(message) { 57 switch (message.name) { 58 case "Content:Click": 59 fillInClickEvent(this, message.data); 60 this.contentAreaClick(message.data); 61 this.notifyClickListeners(message.data); 62 break; 63 } 64 } 65 66 /** 67 * Handles clicks in the content area. 68 * 69 * @param data {Object} object that looks like an Event 70 * @param browser {Element<browser>} 71 */ 72 contentAreaClick(data) { 73 // This is heavily based on contentAreaClick from browser.js (Bug 903016) 74 // The data is set up in a way to look like an Event. 75 let browser = this.manager.browsingContext.top.embedderElement; 76 if (!browser) { 77 // Can be null if the tab disappeared by the time we got the message. 78 // Just bail. 79 return; 80 } 81 let window = browser.ownerGlobal; 82 83 // If the browser is not in a place where we can open links, bail out. 84 // This can happen in osx sheets, dialogs, etc. that are not browser 85 // windows. Specifically the payments UI is in an osx sheet. 86 if (window.openLinkIn === undefined) { 87 return; 88 } 89 90 // Mark the page as a user followed link. This is done so that history can 91 // distinguish automatic embed visits from user activated ones. For example 92 // pages loaded in frames are embed visits and lost with the session, while 93 // visits across frames should be preserved. 94 try { 95 if (!lazy.PrivateBrowsingUtils.isWindowPrivate(window)) { 96 lazy.PlacesUIUtils.markPageAsFollowedLink(data.href); 97 } 98 } catch (ex) { 99 /* Skip invalid URIs. */ 100 } 101 102 // This part is based on handleLinkClick. 103 var where = lazy.BrowserUtils.whereToOpenLink(data); 104 if (where == "current") { 105 return; 106 } 107 108 // Todo(903022): code for where == save 109 110 let params = { 111 charset: browser.characterSet, 112 referrerInfo: lazy.E10SUtils.deserializeReferrerInfo(data.referrerInfo), 113 isContentWindowPrivate: data.isContentWindowPrivate, 114 originPrincipal: data.originPrincipal, 115 originStoragePrincipal: data.originStoragePrincipal, 116 triggeringPrincipal: data.triggeringPrincipal, 117 policyContainer: data.policyContainer 118 ? lazy.E10SUtils.deserializePolicyContainer(data.policyContainer) 119 : null, 120 frameID: data.frameID, 121 openerBrowser: browser, 122 // The child ensures that untrusted events have a valid user activation. 123 hasValidUserGestureActivation: true, 124 textDirectiveUserActivation: true, 125 triggeringRemoteType: this.manager.domProcess?.remoteType, 126 }; 127 128 if (data.globalHistoryOptions) { 129 params.globalHistoryOptions = data.globalHistoryOptions; 130 } else { 131 params.globalHistoryOptions = { 132 triggeringSponsoredURL: browser.getAttribute("triggeringSponsoredURL"), 133 triggeringSponsoredURLVisitTimeMS: browser.getAttribute( 134 "triggeringSponsoredURLVisitTimeMS" 135 ), 136 triggeringSource: browser.getAttribute("triggeringSource"), 137 }; 138 } 139 140 // The new tab/window must use the same userContextId. 141 if (data.originAttributes.userContextId) { 142 params.userContextId = data.originAttributes.userContextId; 143 } 144 145 params.allowInheritPrincipal = true; 146 147 window.openLinkIn(data.href, where, params); 148 } 149 150 notifyClickListeners(data) { 151 for (let listener of gContentClickListeners) { 152 try { 153 let browser = this.browsingContext.top.embedderElement; 154 155 listener.onContentClick(browser, data); 156 } catch (ex) { 157 console.error(ex); 158 } 159 } 160 } 161 }