tor-browser

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

ContextMenuParent.sys.mjs (8147B)


      1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
      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  E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
     12  FirefoxRelay: "resource://gre/modules/FirefoxRelay.sys.mjs",
     13  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs",
     14 });
     15 
     16 XPCOMUtils.defineLazyServiceGetters(lazy, {
     17  BrowserHandler: ["@mozilla.org/browser/clh;1", Ci.nsIBrowserHandler],
     18 });
     19 
     20 XPCOMUtils.defineLazyPreferenceGetter(
     21  lazy,
     22  "TEXT_FRAGMENTS_ENABLED",
     23  "dom.text_fragments.enabled",
     24  false
     25 );
     26 
     27 export class ContextMenuParent extends JSWindowActorParent {
     28  receiveMessage(message) {
     29    let browser = this.manager.rootFrameLoader.ownerElement;
     30    if (browser.hasAttribute("disablecontextmenu")) {
     31      return;
     32    }
     33 
     34    let win = browser.ownerGlobal;
     35    // It's possible that the <xul:browser> associated with this
     36    // ContextMenu message doesn't belong to a window that actually
     37    // loads nsContextMenu.js. In that case, try to find the chromeEventHandler,
     38    // since that'll likely be the "top" <xul:browser>, and then use its window's
     39    // nsContextMenu instance instead.
     40    if (!win.nsContextMenu) {
     41      let topBrowser = browser.ownerGlobal.docShell.chromeEventHandler;
     42      win = topBrowser.ownerGlobal;
     43    }
     44 
     45    message.data.context.showRelay &&= lazy.FirefoxRelay.isEnabled;
     46 
     47    this.#openContextMenu(message.data, win, browser);
     48  }
     49 
     50  hiding() {
     51    try {
     52      this.sendAsyncMessage("ContextMenu:Hiding", {});
     53    } catch (e) {
     54      // This will throw if the content goes away while the
     55      // context menu is still open.
     56    }
     57  }
     58 
     59  reloadFrame(targetIdentifier, forceReload) {
     60    this.sendAsyncMessage("ContextMenu:ReloadFrame", {
     61      targetIdentifier,
     62      forceReload,
     63    });
     64  }
     65 
     66  getImageText(targetIdentifier) {
     67    return this.sendQuery("ContextMenu:GetImageText", {
     68      targetIdentifier,
     69    });
     70  }
     71 
     72  toggleRevealPassword(targetIdentifier) {
     73    this.sendAsyncMessage("ContextMenu:ToggleRevealPassword", {
     74      targetIdentifier,
     75    });
     76  }
     77 
     78  async useRelayMask(targetIdentifier, origin) {
     79    if (!origin) {
     80      return;
     81    }
     82 
     83    const windowGlobal = this.manager.browsingContext.currentWindowGlobal;
     84    const browser = windowGlobal.rootFrameLoader.ownerElement;
     85    const emailMask = await lazy.FirefoxRelay.generateUsername(browser, origin);
     86    if (emailMask) {
     87      this.sendAsyncMessage("ContextMenu:UseRelayMask", {
     88        targetIdentifier,
     89        emailMask,
     90      });
     91    }
     92  }
     93 
     94  reloadImage(targetIdentifier) {
     95    this.sendAsyncMessage("ContextMenu:ReloadImage", { targetIdentifier });
     96  }
     97 
     98  getFrameTitle(targetIdentifier) {
     99    return this.sendQuery("ContextMenu:GetFrameTitle", { targetIdentifier });
    100  }
    101 
    102  mediaCommand(targetIdentifier, command, data) {
    103    let windowGlobal = this.manager.browsingContext.currentWindowGlobal;
    104    let browser = windowGlobal.rootFrameLoader.ownerElement;
    105    let win = browser.ownerGlobal;
    106    let windowUtils = win.windowUtils;
    107    this.sendAsyncMessage("ContextMenu:MediaCommand", {
    108      targetIdentifier,
    109      command,
    110      data,
    111      handlingUserInput: windowUtils.isHandlingUserInput,
    112    });
    113  }
    114 
    115  canvasToBlobURL(targetIdentifier) {
    116    return this.sendQuery("ContextMenu:Canvas:ToBlobURL", { targetIdentifier });
    117  }
    118 
    119  saveVideoFrameAsImage(targetIdentifier) {
    120    return this.sendQuery("ContextMenu:SaveVideoFrameAsImage", {
    121      targetIdentifier,
    122    });
    123  }
    124 
    125  setAsDesktopBackground(targetIdentifier) {
    126    return this.sendQuery("ContextMenu:SetAsDesktopBackground", {
    127      targetIdentifier,
    128    });
    129  }
    130 
    131  getSearchFieldEngineData(targetIdentifier) {
    132    return this.sendQuery("ContextMenu:SearchFieldEngineData", {
    133      targetIdentifier,
    134    });
    135  }
    136 
    137  getTextDirective() {
    138    return lazy.TEXT_FRAGMENTS_ENABLED
    139      ? this.sendQuery("ContextMenu:GetTextDirective")
    140      : null;
    141  }
    142 
    143  removeAllTextFragments() {
    144    return this.sendQuery("ContextMenu:RemoveAllTextFragments");
    145  }
    146 
    147  /**
    148   * Handles opening of the context menu for the appropraite browser.
    149   *
    150   * @param {object} data
    151   *   The data for the context menu, received from the child.
    152   * @param {DOMWindow} win
    153   *   The window in which the context menu is to be opened.
    154   * @param {Browser} browser
    155   *   The browser the context menu is being opened for.
    156   */
    157  #openContextMenu(data, win, browser) {
    158    if (lazy.BrowserHandler.kiosk) {
    159      // Don't display context menus in kiosk mode
    160      return;
    161    }
    162    let wgp = this.manager;
    163 
    164    if (!wgp.isCurrentGlobal) {
    165      // Don't display context menus for unloaded documents
    166      return;
    167    }
    168 
    169    // NOTE: We don't use `wgp.documentURI` here as we want to use the failed
    170    // channel URI in the case we have loaded an error page.
    171    let documentURIObject = wgp.browsingContext.currentURI;
    172 
    173    let frameReferrerInfo = data.frameReferrerInfo;
    174    if (frameReferrerInfo) {
    175      frameReferrerInfo =
    176        lazy.E10SUtils.deserializeReferrerInfo(frameReferrerInfo);
    177    }
    178 
    179    let linkReferrerInfo = data.linkReferrerInfo;
    180    if (linkReferrerInfo) {
    181      linkReferrerInfo =
    182        lazy.E10SUtils.deserializeReferrerInfo(linkReferrerInfo);
    183    }
    184 
    185    let frameID = lazy.WebNavigationFrames.getFrameId(wgp.browsingContext);
    186 
    187    win.nsContextMenu.contentData = {
    188      context: data.context,
    189      browser,
    190      actor: this,
    191      editFlags: data.editFlags,
    192      spellInfo: data.spellInfo,
    193      principal: wgp.documentPrincipal,
    194      storagePrincipal: wgp.documentStoragePrincipal,
    195      documentURIObject,
    196      docLocation: documentURIObject.spec,
    197      charSet: data.charSet,
    198      referrerInfo: lazy.E10SUtils.deserializeReferrerInfo(data.referrerInfo),
    199      frameReferrerInfo,
    200      linkReferrerInfo,
    201      contentType: data.contentType,
    202      contentDisposition: data.contentDisposition,
    203      frameID,
    204      frameOuterWindowID: frameID,
    205      frameBrowsingContext: wgp.browsingContext,
    206      selectionInfo: data.selectionInfo,
    207      disableSetDesktopBackground: data.disableSetDesktopBackground,
    208      showRelay: data.showRelay,
    209      loginFillInfo: data.loginFillInfo,
    210      userContextId: wgp.browsingContext.originAttributes.userContextId,
    211      webExtContextData: data.webExtContextData,
    212      cookieJarSettings: wgp.cookieJarSettings,
    213    };
    214 
    215    let popup = win.document.getElementById("contentAreaContextMenu");
    216    let context = win.nsContextMenu.contentData.context;
    217 
    218    // Fill in some values in the context from the WindowGlobalParent actor.
    219    context.principal = wgp.documentPrincipal;
    220    context.storagePrincipal = wgp.documentStoragePrincipal;
    221    context.frameID = frameID;
    222    context.frameOuterWindowID = wgp.outerWindowId;
    223    context.frameBrowsingContextID = wgp.browsingContext.id;
    224 
    225    // We don't have access to the original event here, as that happened in
    226    // another process. Therefore we synthesize a new MouseEvent to propagate the
    227    // inputSource to the subsequently triggered popupshowing event.
    228    let newEvent = new PointerEvent("contextmenu", {
    229      bubbles: true,
    230      cancelable: true,
    231      screenX: context.screenXDevPx / win.devicePixelRatio,
    232      screenY: context.screenYDevPx / win.devicePixelRatio,
    233      button: 2,
    234      pointerType: (() => {
    235        switch (context.inputSource) {
    236          case MouseEvent.MOZ_SOURCE_MOUSE:
    237            return "mouse";
    238          case MouseEvent.MOZ_SOURCE_PEN:
    239            return "pen";
    240          case MouseEvent.MOZ_SOURCE_ERASER:
    241            return "eraser";
    242          case MouseEvent.MOZ_SOURCE_CURSOR:
    243            return "cursor";
    244          case MouseEvent.MOZ_SOURCE_TOUCH:
    245            return "touch";
    246          case MouseEvent.MOZ_SOURCE_KEYBOARD:
    247            return "keyboard";
    248          default:
    249            return "";
    250        }
    251      })(),
    252    });
    253    popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent);
    254  }
    255 }