248 lines
7.9 KiB
JavaScript
248 lines
7.9 KiB
JavaScript
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
|
|
/* 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/. */
|
|
|
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
|
|
FirefoxRelay: "resource://gre/modules/FirefoxRelay.sys.mjs",
|
|
WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs",
|
|
});
|
|
|
|
XPCOMUtils.defineLazyServiceGetters(lazy, {
|
|
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
|
|
});
|
|
|
|
export class ContextMenuParent extends JSWindowActorParent {
|
|
receiveMessage(message) {
|
|
let browser = this.manager.rootFrameLoader.ownerElement;
|
|
let win = browser.ownerGlobal;
|
|
// It's possible that the <xul:browser> associated with this
|
|
// ContextMenu message doesn't belong to a window that actually
|
|
// loads nsContextMenu.js. In that case, try to find the chromeEventHandler,
|
|
// since that'll likely be the "top" <xul:browser>, and then use its window's
|
|
// nsContextMenu instance instead.
|
|
if (!win.nsContextMenu) {
|
|
let topBrowser = browser.ownerGlobal.docShell.chromeEventHandler;
|
|
win = topBrowser.ownerGlobal;
|
|
}
|
|
|
|
message.data.context.showRelay &&= lazy.FirefoxRelay.isEnabled;
|
|
|
|
this.#openContextMenu(message.data, win, browser);
|
|
}
|
|
|
|
hiding() {
|
|
try {
|
|
this.sendAsyncMessage("ContextMenu:Hiding", {});
|
|
} catch (e) {
|
|
// This will throw if the content goes away while the
|
|
// context menu is still open.
|
|
}
|
|
}
|
|
|
|
reloadFrame(targetIdentifier, forceReload) {
|
|
this.sendAsyncMessage("ContextMenu:ReloadFrame", {
|
|
targetIdentifier,
|
|
forceReload,
|
|
});
|
|
}
|
|
|
|
getImageText(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:GetImageText", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
toggleRevealPassword(targetIdentifier) {
|
|
this.sendAsyncMessage("ContextMenu:ToggleRevealPassword", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
async useRelayMask(targetIdentifier, origin) {
|
|
if (!origin) {
|
|
return;
|
|
}
|
|
|
|
const windowGlobal = this.manager.browsingContext.currentWindowGlobal;
|
|
const browser = windowGlobal.rootFrameLoader.ownerElement;
|
|
const emailMask = await lazy.FirefoxRelay.generateUsername(browser, origin);
|
|
if (emailMask) {
|
|
this.sendAsyncMessage("ContextMenu:UseRelayMask", {
|
|
targetIdentifier,
|
|
emailMask,
|
|
});
|
|
}
|
|
}
|
|
|
|
reloadImage(targetIdentifier) {
|
|
this.sendAsyncMessage("ContextMenu:ReloadImage", { targetIdentifier });
|
|
}
|
|
|
|
getFrameTitle(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:GetFrameTitle", { targetIdentifier });
|
|
}
|
|
|
|
mediaCommand(targetIdentifier, command, data) {
|
|
let windowGlobal = this.manager.browsingContext.currentWindowGlobal;
|
|
let browser = windowGlobal.rootFrameLoader.ownerElement;
|
|
let win = browser.ownerGlobal;
|
|
let windowUtils = win.windowUtils;
|
|
this.sendAsyncMessage("ContextMenu:MediaCommand", {
|
|
targetIdentifier,
|
|
command,
|
|
data,
|
|
handlingUserInput: windowUtils.isHandlingUserInput,
|
|
});
|
|
}
|
|
|
|
canvasToBlobURL(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:Canvas:ToBlobURL", { targetIdentifier });
|
|
}
|
|
|
|
saveVideoFrameAsImage(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:SaveVideoFrameAsImage", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
setAsDesktopBackground(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:SetAsDesktopBackground", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
getSearchFieldBookmarkData(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:SearchFieldBookmarkData", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
getSearchFieldEngineData(targetIdentifier) {
|
|
return this.sendQuery("ContextMenu:SearchFieldEngineData", {
|
|
targetIdentifier,
|
|
});
|
|
}
|
|
|
|
getTextDirective() {
|
|
return this.sendQuery("ContextMenu:GetTextDirective");
|
|
}
|
|
|
|
removeAllTextFragments() {
|
|
return this.sendQuery("ContextMenu:RemoveAllTextFragments");
|
|
}
|
|
|
|
/**
|
|
* Handles opening of the context menu for the appropraite browser.
|
|
*
|
|
* @param {object} data
|
|
* The data for the context menu, received from the child.
|
|
* @param {DOMWindow} win
|
|
* The window in which the context menu is to be opened.
|
|
* @param {Browser} browser
|
|
* The browser the context menu is being opened for.
|
|
*/
|
|
#openContextMenu(data, win, browser) {
|
|
if (lazy.BrowserHandler.kiosk) {
|
|
// Don't display context menus in kiosk mode
|
|
return;
|
|
}
|
|
let wgp = this.manager;
|
|
|
|
if (!wgp.isCurrentGlobal) {
|
|
// Don't display context menus for unloaded documents
|
|
return;
|
|
}
|
|
|
|
// NOTE: We don't use `wgp.documentURI` here as we want to use the failed
|
|
// channel URI in the case we have loaded an error page.
|
|
let documentURIObject = wgp.browsingContext.currentURI;
|
|
|
|
let frameReferrerInfo = data.frameReferrerInfo;
|
|
if (frameReferrerInfo) {
|
|
frameReferrerInfo =
|
|
lazy.E10SUtils.deserializeReferrerInfo(frameReferrerInfo);
|
|
}
|
|
|
|
let linkReferrerInfo = data.linkReferrerInfo;
|
|
if (linkReferrerInfo) {
|
|
linkReferrerInfo =
|
|
lazy.E10SUtils.deserializeReferrerInfo(linkReferrerInfo);
|
|
}
|
|
|
|
let frameID = lazy.WebNavigationFrames.getFrameId(wgp.browsingContext);
|
|
|
|
win.nsContextMenu.contentData = {
|
|
context: data.context,
|
|
browser,
|
|
actor: this,
|
|
editFlags: data.editFlags,
|
|
spellInfo: data.spellInfo,
|
|
principal: wgp.documentPrincipal,
|
|
storagePrincipal: wgp.documentStoragePrincipal,
|
|
documentURIObject,
|
|
docLocation: documentURIObject.spec,
|
|
charSet: data.charSet,
|
|
referrerInfo: lazy.E10SUtils.deserializeReferrerInfo(data.referrerInfo),
|
|
frameReferrerInfo,
|
|
linkReferrerInfo,
|
|
contentType: data.contentType,
|
|
contentDisposition: data.contentDisposition,
|
|
frameID,
|
|
frameOuterWindowID: frameID,
|
|
frameBrowsingContext: wgp.browsingContext,
|
|
selectionInfo: data.selectionInfo,
|
|
disableSetDesktopBackground: data.disableSetDesktopBackground,
|
|
showRelay: data.showRelay,
|
|
loginFillInfo: data.loginFillInfo,
|
|
userContextId: wgp.browsingContext.originAttributes.userContextId,
|
|
webExtContextData: data.webExtContextData,
|
|
cookieJarSettings: wgp.cookieJarSettings,
|
|
};
|
|
|
|
let popup = win.document.getElementById("contentAreaContextMenu");
|
|
let context = win.nsContextMenu.contentData.context;
|
|
|
|
// Fill in some values in the context from the WindowGlobalParent actor.
|
|
context.principal = wgp.documentPrincipal;
|
|
context.storagePrincipal = wgp.documentStoragePrincipal;
|
|
context.frameID = frameID;
|
|
context.frameOuterWindowID = wgp.outerWindowId;
|
|
context.frameBrowsingContextID = wgp.browsingContext.id;
|
|
|
|
// We don't have access to the original event here, as that happened in
|
|
// another process. Therefore we synthesize a new MouseEvent to propagate the
|
|
// inputSource to the subsequently triggered popupshowing event.
|
|
let newEvent = new PointerEvent("contextmenu", {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
screenX: context.screenXDevPx / win.devicePixelRatio,
|
|
screenY: context.screenYDevPx / win.devicePixelRatio,
|
|
button: 2,
|
|
pointerType: (() => {
|
|
switch (context.inputSource) {
|
|
case MouseEvent.MOZ_SOURCE_MOUSE:
|
|
return "mouse";
|
|
case MouseEvent.MOZ_SOURCE_PEN:
|
|
return "pen";
|
|
case MouseEvent.MOZ_SOURCE_ERASER:
|
|
return "eraser";
|
|
case MouseEvent.MOZ_SOURCE_CURSOR:
|
|
return "cursor";
|
|
case MouseEvent.MOZ_SOURCE_TOUCH:
|
|
return "touch";
|
|
case MouseEvent.MOZ_SOURCE_KEYBOARD:
|
|
return "keyboard";
|
|
default:
|
|
return "";
|
|
}
|
|
})(),
|
|
});
|
|
popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent);
|
|
}
|
|
}
|