ClickHandlerChild.sys.mjs (6095B)
1 /* -*- 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 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 7 8 const lazy = {}; 9 10 ChromeUtils.defineESModuleGetters(lazy, { 11 BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", 12 E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs", 13 }); 14 15 XPCOMUtils.defineLazyPreferenceGetter( 16 lazy, 17 "autoscrollEnabled", 18 "general.autoScroll", 19 true 20 ); 21 22 XPCOMUtils.defineLazyPreferenceGetter( 23 lazy, 24 "blockJavascript", 25 "browser.link.alternative_click.block_javascript", 26 true 27 ); 28 29 export class MiddleMousePasteHandlerChild extends JSWindowActorChild { 30 handleEvent(clickEvent) { 31 if ( 32 clickEvent.defaultPrevented || 33 clickEvent.button != 1 || 34 lazy.autoscrollEnabled 35 ) { 36 return; 37 } 38 this.manager 39 .getActor("ClickHandler") 40 .handleClickEvent( 41 clickEvent, 42 /* is from middle mouse paste handler */ true 43 ); 44 } 45 46 onProcessedClick(data) { 47 this.sendAsyncMessage("MiddleClickPaste", data); 48 } 49 } 50 51 export class ClickHandlerChild extends JSWindowActorChild { 52 handleEvent(wrapperEvent) { 53 this.handleClickEvent(wrapperEvent.sourceEvent); 54 } 55 56 handleClickEvent(event, isFromMiddleMousePasteHandler = false) { 57 if (event.defaultPrevented || event.button == 2) { 58 return; 59 } 60 // Don't do anything on editable things, we shouldn't open links in 61 // contenteditables, and editor needs to possibly handle middlemouse paste 62 let composedTarget = event.composedTarget; 63 if ( 64 composedTarget.isContentEditable || 65 (composedTarget.ownerDocument && 66 composedTarget.ownerDocument.designMode == "on") || 67 ChromeUtils.getClassName(composedTarget) == "HTMLInputElement" || 68 ChromeUtils.getClassName(composedTarget) == "HTMLTextAreaElement" 69 ) { 70 return; 71 } 72 73 let originalTarget = event.originalTarget; 74 let ownerDoc = originalTarget.ownerDocument; 75 if (!ownerDoc) { 76 return; 77 } 78 79 // Handle click events from about pages 80 if (event.button == 0) { 81 if (ownerDoc.documentURI.startsWith("about:blocked")) { 82 return; 83 } 84 } 85 86 // For untrusted events, require a valid transient user gesture activation. 87 if (!event.isTrusted && !ownerDoc.hasValidTransientUserGestureActivation) { 88 return; 89 } 90 91 let [href, node, principal] = 92 lazy.BrowserUtils.hrefAndLinkNodeForClickEvent(event); 93 94 let policyContainer = ownerDoc.policyContainer; 95 if (policyContainer) { 96 policyContainer = 97 lazy.E10SUtils.serializePolicyContainer(policyContainer); 98 } 99 100 let referrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance( 101 Ci.nsIReferrerInfo 102 ); 103 if (node) { 104 referrerInfo.initWithElement(node); 105 } else { 106 referrerInfo.initWithDocument(ownerDoc); 107 } 108 referrerInfo = lazy.E10SUtils.serializeReferrerInfo(referrerInfo); 109 110 let json = { 111 button: event.button, 112 shiftKey: event.shiftKey, 113 ctrlKey: event.ctrlKey, 114 metaKey: event.metaKey, 115 altKey: event.altKey, 116 href: null, 117 title: null, 118 policyContainer, 119 referrerInfo, 120 }; 121 122 if (href && !isFromMiddleMousePasteHandler) { 123 if ( 124 lazy.blockJavascript && 125 Services.io.extractScheme(href) == "javascript" 126 ) { 127 // We don't want to open new tabs or windows for javascript: links. 128 return; 129 } 130 131 try { 132 Services.scriptSecurityManager.checkLoadURIStrWithPrincipal( 133 principal, 134 href 135 ); 136 } catch (e) { 137 return; 138 } 139 140 if ( 141 !event.isTrusted && 142 lazy.BrowserUtils.whereToOpenLink(event) != "current" 143 ) { 144 // If we'll open the link, we want to consume the user gesture 145 // activation to ensure that we don't allow multiple links to open 146 // from one user gesture. 147 // Avoid doing so for links opened in the current tab, which get 148 // handled later, by gecko, as otherwise its popup blocker will stop 149 // the link from opening. 150 // We will do the same check (whereToOpenLink) again in the parent and 151 // avoid handling the click for such links... but we still need the 152 // click information in the parent because otherwise places link 153 // tracking breaks. (bug 1742894 tracks improving this.) 154 ownerDoc.consumeTransientUserGestureActivation(); 155 // We don't care about the return value because we already checked that 156 // hasValidTransientUserGestureActivation was true earlier in this 157 // function. 158 } 159 160 json.href = href; 161 if (node) { 162 json.title = node.getAttribute("title"); 163 } 164 165 if ( 166 (ownerDoc.URL === "about:newtab" || ownerDoc.URL === "about:home") && 167 node.dataset.isSponsoredLink === "true" 168 ) { 169 json.globalHistoryOptions = { 170 triggeringSource: "newtab", 171 triggeringSponsoredURL: href, 172 }; 173 } 174 175 // If a link element is clicked with middle button, user wants to open 176 // the link somewhere rather than pasting clipboard content. Therefore, 177 // when it's clicked with middle button, we should prevent multiple 178 // actions here to avoid leaking clipboard content unexpectedly. 179 // Note that whether the link will work actually or not does not matter 180 // because in this case, user does not intent to paste clipboard content. 181 // We also need to do this to prevent multiple tabs opening if there are 182 // nested link elements. 183 event.preventMultipleActions(); 184 185 this.sendAsyncMessage("Content:Click", json); 186 } 187 188 // This might be middle mouse navigation, in which case pass this back: 189 if (!href && event.button == 1 && isFromMiddleMousePasteHandler) { 190 this.manager.getActor("MiddleMousePasteHandler").onProcessedClick(json); 191 } 192 } 193 }