Add ability to prevent window content being captured by other apps (Desktop) (#30098)
* Add ability to prevent window content being captured by other apps (Desktop) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Increase coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Increase coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
committed by
GitHub
parent
3e8599bba0
commit
2b24232f14
6
src/@types/global.d.ts
vendored
6
src/@types/global.d.ts
vendored
@@ -128,13 +128,19 @@ declare global {
|
||||
}
|
||||
|
||||
interface Electron {
|
||||
// Legacy
|
||||
on(channel: ElectronChannel, listener: (event: Event, ...args: any[]) => void): void;
|
||||
send(channel: ElectronChannel, ...args: any[]): void;
|
||||
// Initialisation
|
||||
initialise(): Promise<{
|
||||
protocol: string;
|
||||
sessionId: string;
|
||||
config: IConfigOptions;
|
||||
supportedSettings: Record<string, boolean>;
|
||||
}>;
|
||||
// Settings
|
||||
setSettingValue(settingName: string, value: any): Promise<void>;
|
||||
getSettingValue(settingName: string): Promise<any>;
|
||||
}
|
||||
|
||||
interface DesktopCapturerSource {
|
||||
|
||||
@@ -357,6 +357,12 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||
appName: SdkConfig.get().brand,
|
||||
})}
|
||||
/>
|
||||
<SettingsFlag
|
||||
name="Electron.enableContentProtection"
|
||||
level={SettingLevel.PLATFORM}
|
||||
hideIfCannotSet
|
||||
label={_t("settings|preferences|Electron.enableContentProtection")}
|
||||
/>
|
||||
<SettingsFlag name="Electron.alwaysShowMenuBar" level={SettingLevel.PLATFORM} hideIfCannotSet />
|
||||
<SettingsFlag name="Electron.autoLaunch" level={SettingLevel.PLATFORM} hideIfCannotSet />
|
||||
<SettingsFlag name="Electron.warnBeforeExit" level={SettingLevel.PLATFORM} hideIfCannotSet />
|
||||
|
||||
@@ -2808,6 +2808,7 @@
|
||||
"voip": "Audio and Video calls"
|
||||
},
|
||||
"preferences": {
|
||||
"Electron.enableContentProtection": "Prevent the window contents from being captured by other apps",
|
||||
"Electron.enableHardwareAcceleration": "Enable hardware acceleration (restart %(appName)s to take effect)",
|
||||
"always_show_menu_bar": "Always show the window menu bar",
|
||||
"autocomplete_delay": "Autocomplete delay (ms)",
|
||||
|
||||
@@ -349,6 +349,7 @@ export interface Settings {
|
||||
"Electron.alwaysShowMenuBar": IBaseSetting<boolean>;
|
||||
"Electron.showTrayIcon": IBaseSetting<boolean>;
|
||||
"Electron.enableHardwareAcceleration": IBaseSetting<boolean>;
|
||||
"Electron.enableContentProtection": IBaseSetting<boolean>;
|
||||
"mediaPreviewConfig": IBaseSetting<MediaPreviewConfig>;
|
||||
"Developer.elementCallUrl": IBaseSetting<string>;
|
||||
}
|
||||
@@ -1383,6 +1384,11 @@ export const SETTINGS: Settings = {
|
||||
displayName: _td("settings|preferences|enable_hardware_acceleration"),
|
||||
default: true,
|
||||
},
|
||||
"Electron.enableContentProtection": {
|
||||
supportedLevels: [SettingLevel.PLATFORM],
|
||||
displayName: _td("settings|preferences|enable_hardware_acceleration"),
|
||||
default: false,
|
||||
},
|
||||
"Developer.elementCallUrl": {
|
||||
supportedLevels: [SettingLevel.DEVICE],
|
||||
displayName: _td("devtools|settings|elementCallUrl"),
|
||||
|
||||
@@ -52,8 +52,6 @@ interface SquirrelUpdate {
|
||||
|
||||
const SSO_ID_KEY = "element-desktop-ssoid";
|
||||
|
||||
const isMac = navigator.platform.toUpperCase().includes("MAC");
|
||||
|
||||
function platformFriendlyName(): string {
|
||||
// used to use window.process but the same info is available here
|
||||
if (navigator.userAgent.includes("Macintosh")) {
|
||||
@@ -73,13 +71,6 @@ function platformFriendlyName(): string {
|
||||
}
|
||||
}
|
||||
|
||||
function onAction(payload: ActionPayload): void {
|
||||
// Whitelist payload actions, no point sending most across
|
||||
if (["call_state"].includes(payload.action)) {
|
||||
window.electron!.send("app_onAction", payload);
|
||||
}
|
||||
}
|
||||
|
||||
function getUpdateCheckStatus(status: boolean | string): UpdateStatus {
|
||||
if (status === true) {
|
||||
return { status: UpdateCheckStatus.Downloading };
|
||||
@@ -97,9 +88,11 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
private readonly ipc = new IPCManager("ipcCall", "ipcReply");
|
||||
private readonly eventIndexManager: BaseEventIndexManager = new SeshatIndexManager();
|
||||
private readonly initialised: Promise<void>;
|
||||
private readonly electron: Electron;
|
||||
private protocol!: string;
|
||||
private sessionId!: string;
|
||||
private config!: IConfigOptions;
|
||||
private supportedSettings?: Record<string, boolean>;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
@@ -107,15 +100,15 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
if (!window.electron) {
|
||||
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||
}
|
||||
this.electron = window.electron;
|
||||
|
||||
dis.register(onAction);
|
||||
/*
|
||||
IPC Call `check_updates` returns:
|
||||
true if there is an update available
|
||||
false if there is not
|
||||
or the error if one is encountered
|
||||
*/
|
||||
window.electron.on("check_updates", (event, status) => {
|
||||
this.electron.on("check_updates", (event, status) => {
|
||||
dis.dispatch<CheckUpdatesPayload>({
|
||||
action: Action.CheckUpdates,
|
||||
...getUpdateCheckStatus(status),
|
||||
@@ -124,44 +117,44 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
|
||||
// `userAccessToken` (IPC) is requested by the main process when appending authentication
|
||||
// to media downloads. A reply is sent over the same channel.
|
||||
window.electron.on("userAccessToken", () => {
|
||||
window.electron!.send("userAccessToken", MatrixClientPeg.get()?.getAccessToken());
|
||||
this.electron.on("userAccessToken", () => {
|
||||
this.electron.send("userAccessToken", MatrixClientPeg.get()?.getAccessToken());
|
||||
});
|
||||
|
||||
// `homeserverUrl` (IPC) is requested by the main process. A reply is sent over the same channel.
|
||||
window.electron.on("homeserverUrl", () => {
|
||||
window.electron!.send("homeserverUrl", MatrixClientPeg.get()?.getHomeserverUrl());
|
||||
this.electron.on("homeserverUrl", () => {
|
||||
this.electron.send("homeserverUrl", MatrixClientPeg.get()?.getHomeserverUrl());
|
||||
});
|
||||
|
||||
// `serverSupportedVersions` is requested by the main process when it needs to know if the
|
||||
// server supports a particular version. This is primarily used to detect authenticated media
|
||||
// support. A reply is sent over the same channel.
|
||||
window.electron.on("serverSupportedVersions", async () => {
|
||||
window.electron!.send("serverSupportedVersions", await MatrixClientPeg.get()?.getVersions());
|
||||
this.electron.on("serverSupportedVersions", async () => {
|
||||
this.electron.send("serverSupportedVersions", await MatrixClientPeg.get()?.getVersions());
|
||||
});
|
||||
|
||||
// try to flush the rageshake logs to indexeddb before quit.
|
||||
window.electron.on("before-quit", function () {
|
||||
this.electron.on("before-quit", function () {
|
||||
logger.log("element-desktop closing");
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
window.electron.on("update-downloaded", this.onUpdateDownloaded);
|
||||
this.electron.on("update-downloaded", this.onUpdateDownloaded);
|
||||
|
||||
window.electron.on("preferences", () => {
|
||||
this.electron.on("preferences", () => {
|
||||
dis.fire(Action.ViewUserSettings);
|
||||
});
|
||||
|
||||
window.electron.on("userDownloadCompleted", (ev, { id, name }) => {
|
||||
this.electron.on("userDownloadCompleted", (ev, { id, name }) => {
|
||||
const key = `DOWNLOAD_TOAST_${id}`;
|
||||
|
||||
const onAccept = (): void => {
|
||||
window.electron!.send("userDownloadAction", { id, open: true });
|
||||
this.electron.send("userDownloadAction", { id, open: true });
|
||||
ToastStore.sharedInstance().dismissToast(key);
|
||||
};
|
||||
|
||||
const onDismiss = (): void => {
|
||||
window.electron!.send("userDownloadAction", { id });
|
||||
this.electron.send("userDownloadAction", { id });
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
@@ -180,7 +173,7 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
});
|
||||
});
|
||||
|
||||
window.electron.on("openDesktopCapturerSourcePicker", async () => {
|
||||
this.electron.on("openDesktopCapturerSourcePicker", async () => {
|
||||
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||
const [source] = await finished;
|
||||
// getDisplayMedia promise does not return if no dummy is passed here as source
|
||||
@@ -192,11 +185,20 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
this.initialised = this.initialise();
|
||||
}
|
||||
|
||||
protected onAction(payload: ActionPayload): void {
|
||||
super.onAction(payload);
|
||||
// Whitelist payload actions, no point sending most across
|
||||
if (["call_state"].includes(payload.action)) {
|
||||
this.electron.send("app_onAction", payload);
|
||||
}
|
||||
}
|
||||
|
||||
private async initialise(): Promise<void> {
|
||||
const { protocol, sessionId, config } = await window.electron!.initialise();
|
||||
const { protocol, sessionId, config, supportedSettings } = await this.electron.initialise();
|
||||
this.protocol = protocol;
|
||||
this.sessionId = sessionId;
|
||||
this.config = config;
|
||||
this.supportedSettings = supportedSettings;
|
||||
}
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||
@@ -248,7 +250,7 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
|
||||
window.electron!.send("setBadgeCount", count);
|
||||
this.electron.send("setBadgeCount", count);
|
||||
}
|
||||
|
||||
public supportsNotifications(): boolean {
|
||||
@@ -288,7 +290,7 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
}
|
||||
|
||||
public loudNotification(ev: MatrixEvent, room: Room): void {
|
||||
window.electron!.send("loudNotification");
|
||||
this.electron.send("loudNotification");
|
||||
}
|
||||
|
||||
public needsUrlTooltips(): boolean {
|
||||
@@ -300,21 +302,16 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
}
|
||||
|
||||
public supportsSetting(settingName?: string): boolean {
|
||||
switch (settingName) {
|
||||
case "Electron.showTrayIcon": // Things other than Mac support tray icons
|
||||
case "Electron.alwaysShowMenuBar": // This isn't relevant on Mac as Menu bars don't live in the app window
|
||||
return !isMac;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
if (settingName === undefined) return true;
|
||||
return this.supportedSettings?.[settingName] === true;
|
||||
}
|
||||
|
||||
public getSettingValue(settingName: string): Promise<any> {
|
||||
return this.ipc.call("getSettingValue", settingName);
|
||||
return this.electron.getSettingValue(settingName);
|
||||
}
|
||||
|
||||
public setSettingValue(settingName: string, value: any): Promise<void> {
|
||||
return this.ipc.call("setSettingValue", settingName, value);
|
||||
return this.electron.setSettingValue(settingName, value);
|
||||
}
|
||||
|
||||
public async canSelfUpdate(): Promise<boolean> {
|
||||
@@ -324,14 +321,14 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
|
||||
public startUpdateCheck(): void {
|
||||
super.startUpdateCheck();
|
||||
window.electron!.send("check_updates");
|
||||
this.electron.send("check_updates");
|
||||
}
|
||||
|
||||
public installUpdate(): void {
|
||||
// IPC to the main process to install the update, since quitAndInstall
|
||||
// doesn't fire the before-quit event so the main process needs to know
|
||||
// it should exit.
|
||||
window.electron!.send("install_update");
|
||||
this.electron.send("install_update");
|
||||
}
|
||||
|
||||
public getDefaultDeviceDisplayName(): string {
|
||||
|
||||
Reference in New Issue
Block a user