tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }