Skip to content

Commit a6c59e6

Browse files
Remove context isolation
I couldn't get the application to run unless I unset contextIsolation. So here is the application split into preload and node files. Makes iterop a bit more painful. But everything seems to work, at least as far as I tested. I think the interop stuff makes error logging a bit painful, so I may have missed the mark a bit. I can say for certain that clicking notifications works as expected. The mutation observer stuff I'm a little less clear on. (But the functions are called when I used console.log statements). I completely removed the profileImage stuff, it doesn't appear to be necessary. Maybe its just Linux, but the notification rendered the NotificationsOptions.icon that was provided by the google SPA just fine without all the cache code.
1 parent 68dffa3 commit a6c59e6

File tree

9 files changed

+216
-174
lines changed

9 files changed

+216
-174
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"main": "app/background.js",
2020
"scripts": {
21-
"start": "ELECTRON_ENABLE_LOGGING=1 ELECTRON_DEBUG_NOTIFICATIONS=true yarn build:dev && cross-env NODE_ENV=development electron --enable-logging ./app/background.js",
21+
"start": "yarn build:dev && cross-env NODE_ENV=development electron ./app/background.js",
2222
"dist": "yarn build && yarn package",
2323
"build": "webpack --mode=production",
2424
"build:dev": "webpack --mode=development",

src/background.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { app, Event as ElectronEvent, ipcMain, shell } from "electron";
1+
import { app, Event as ElectronEvent, ipcMain, ipcRenderer, shell } from "electron";
22
import { BrowserWindow } from "electron/main";
33
import path from "path";
44
import process from "process";
@@ -8,6 +8,7 @@ import { MenuManager } from "./helpers/menuManager";
88
import { setSettingsFlushEnabled, settings } from "./helpers/settings";
99
import { Conversation, TrayManager } from "./helpers/trayManager";
1010
import { popupContextMenu } from "./menu/contextMenu";
11+
import fs from "fs";
1112

1213
const {
1314
autoHideMenuEnabled,
@@ -75,8 +76,6 @@ if (gotTheLock) {
7576
: undefined,
7677
titleBarStyle: IS_MAC ? "hiddenInset" : "default",
7778
webPreferences: {
78-
nodeIntegration: true,
79-
contextIsolation: false,
8079
preload: IS_DEV
8180
? path.resolve(app.getAppPath(), "bridge.js")
8281
: path.resolve(app.getAppPath(), "app", "bridge.js"),
@@ -197,4 +196,9 @@ if (gotTheLock) {
197196
ipcMain.on("set-recent-conversations", (_event, data: Conversation[]) => {
198197
trayManager.setRecentConversations(data);
199198
});
199+
200+
ipcMain.handle("get-icon", () => {
201+
var bitmap = fs.readFileSync(path.resolve(RESOURCES_PATH, "icons", "64x64.png"));
202+
return Buffer.from(bitmap).toString('base64');
203+
})
200204
}

src/bridge.ts

Lines changed: 122 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,142 @@
1-
import { ipcRenderer } from "electron";
2-
import path from "path";
1+
import { contextBridge, ipcRenderer, webFrame } from "electron";
2+
33
import {
44
INITIAL_ICON_IMAGE,
55
IS_MAC,
6-
RESOURCES_PATH,
7-
} from "./helpers/constants";
6+
} from "./preload/constants_preload";
87
import {
98
createRecentThreadObserver,
109
createUnreadObserver,
1110
focusFunctions,
1211
recentThreadObserver,
13-
} from "./helpers/observers";
14-
import { getProfileImg } from "./helpers/profileImage";
15-
16-
window.addEventListener("load", () => {
17-
if (IS_MAC) {
18-
const titlebarStyle = `#amd-titlebar {
19-
-webkit-app-region: drag;
20-
position: fixed;
21-
width: 100%;
22-
height: 64px;
23-
top: 0;
24-
left: 0;
25-
background: none;
26-
pointer-events: none;
27-
}`;
12+
} from "./preload/observers";
2813

29-
document.body.appendChild(
30-
Object.assign(document.createElement("style"), {
31-
textContent: titlebarStyle,
32-
})
33-
);
34-
35-
const titlebar = document.createElement("div");
36-
titlebar.id = "amd-titlebar";
37-
document.querySelector("mw-app")?.parentNode?.prepend(titlebar);
14+
declare global {
15+
interface Window {
16+
interop: any;
3817
}
39-
40-
const conversationListObserver = new MutationObserver(() => {
41-
if (document.querySelector("mws-conversations-list") != null) {
42-
createUnreadObserver();
43-
createRecentThreadObserver();
44-
45-
// keep trying to get an image that isnt blank until they load
46-
const interval = setInterval(() => {
47-
const conversation = document.body.querySelector(
48-
"mws-conversation-list-item"
49-
);
50-
if (conversation) {
51-
const canvas = conversation.querySelector(
52-
"a div.avatar-container canvas"
53-
) as HTMLCanvasElement | null;
54-
55-
if (canvas != null && canvas.toDataURL() != INITIAL_ICON_IMAGE) {
56-
recentThreadObserver();
57-
// refresh for profile image loads after letter loads.
58-
setTimeout(recentThreadObserver, 3000);
59-
clearInterval(interval);
60-
}
61-
}
62-
}, 250);
63-
conversationListObserver.disconnect();
18+
}
19+
20+
const preload_init = () => {
21+
22+
if (IS_MAC) {
23+
const titlebarStyle = `#amd-titlebar {
24+
-webkit-app-region: drag;
25+
position: fixed;
26+
width: 100%;
27+
height: 64px;
28+
top: 0;
29+
left: 0;
30+
background: none;
31+
pointer-events: none;
32+
}`;
33+
34+
document.body.appendChild(
35+
Object.assign(document.createElement("style"), {
36+
textContent: titlebarStyle,
37+
})
38+
);
39+
40+
const titlebar = document.createElement("div");
41+
titlebar.id = "amd-titlebar";
42+
document.querySelector("mw-app")?.parentNode?.prepend(titlebar);
6443
}
44+
45+
const conversationListObserver = new MutationObserver(() => {
46+
if (document.querySelector("mws-conversations-list") != null) {
47+
createUnreadObserver();
48+
createRecentThreadObserver();
49+
50+
// keep trying to get an image that isnt blank until they load
51+
const interval = setInterval(() => {
52+
const conversation = document.body.querySelector(
53+
"mws-conversation-list-item"
54+
);
55+
if (conversation) {
56+
const canvas = conversation.querySelector(
57+
"a div.avatar-container canvas"
58+
) as HTMLCanvasElement | null;
59+
60+
if (canvas != null && canvas.toDataURL() != INITIAL_ICON_IMAGE) {
61+
recentThreadObserver();
62+
// refresh for profile image loads after letter loads.
63+
setTimeout(recentThreadObserver, 3000);
64+
clearInterval(interval);
65+
}
66+
}
67+
}, 250);
68+
conversationListObserver.disconnect();
69+
}
70+
71+
const title = document.head.querySelector("title");
72+
if (title != null) {
73+
title.innerText = "Android Messages";
74+
}
75+
});
76+
77+
conversationListObserver.observe(document.body, {
78+
attributes: false,
79+
subtree: true,
80+
childList: true,
81+
});
82+
}
6583

66-
const title = document.head.querySelector("title");
67-
if (title != null) {
68-
title.innerText = "Android Messages";
69-
}
70-
});
71-
72-
conversationListObserver.observe(document.body, {
73-
attributes: false,
74-
subtree: true,
75-
childList: true,
76-
});
84+
ipcRenderer.on("focus-conversation", (event, i) => {
85+
focusFunctions[i]();
7786
});
7887

79-
const OldNotification = window.Notification;
80-
81-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
82-
// @ts-ignore
83-
window.Notification = function (title: string, options: NotificationOptions) {
84-
const icon = getProfileImg(title);
85-
86-
const hideContent = ipcRenderer.sendSync("should-hide-notification-content");
87-
88-
const notificationOpts: NotificationOptions = hideContent
89-
? {
90-
body: "Click to open",
91-
icon: path.resolve(RESOURCES_PATH, "icons", "64x64.png"),
92-
}
93-
: {
94-
icon: icon?.toDataURL(),
95-
body: options.body || "",
96-
};
97-
98-
const newTitle = hideContent ? "New Message" : title;
99-
const notification = new OldNotification(newTitle, notificationOpts);
100-
notification.addEventListener("click", () => {
88+
contextBridge.exposeInMainWorld("interop", {
89+
show_main_window: () => {
10190
ipcRenderer.send("show-main-window");
102-
document.dispatchEvent(new Event("focus"));
91+
},
92+
flash_main: () => {
93+
ipcRenderer.send("flash-main-window-if-not-focused");
94+
},
95+
should_hide: () => {
96+
return ipcRenderer.sendSync("should-hide-notification-content");
97+
},
98+
get_icon: async () => {
99+
const data = await ipcRenderer.invoke("get-icon");
100+
return `data:image/png;base64,${data}`;
101+
},
102+
preload_init,
103+
});
104+
webFrame.executeJavaScript(`
105+
window.addEventListener("load", async () => {
106+
window.interop.preload_init();
107+
window.icon_data_uri = await window.interop.get_icon();
103108
});
104-
ipcRenderer.send("flash-main-window-if-not-focused");
105-
return notification;
109+
`);
110+
webFrame.executeJavaScript(`window.OldNotification = window.Notification;
111+
window.Notification = function (title, options) {
112+
try {
113+
const hideContent = window.interop.should_hide();
114+
115+
const notificationOpts = hideContent
116+
? {
117+
body: "Click to open",
118+
icon: window.icon_data_uri
119+
}
120+
: {
121+
body: options?.body || "",
122+
icon: options?.icon
123+
};
124+
125+
const newTitle = hideContent ? "New Message" : title;
126+
const notification = new window.OldNotification(newTitle, notificationOpts);
127+
notification.addEventListener("click", () => {
128+
window.interop.show_main_window();
129+
document.dispatchEvent(new Event("focus"));
130+
});
131+
window.interop.flash_main();
132+
return notification;
133+
} catch (e) {
134+
console.error(e);
135+
console.trace();
136+
}
106137
};
107138
108-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
109-
//@ts-ignore
110139
window.Notification.permission = "granted";
111140
window.Notification.requestPermission = async () => "granted";
112-
113-
window.module.exports = null;
114-
115-
ipcRenderer.on("focus-conversation", (event, i) => {
116-
focusFunctions[i]();
117-
});
141+
`);
142+
contextBridge.exposeInMainWorld("module", {exports: null});

src/helpers/constants.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,4 @@ export const SETTINGS_FILE = (): string => {
2323
*/
2424
export const UUID_NAMESPACE = "ddf09da3-3df8-4417-ae3b-62d3ed4bfb72";
2525

26-
/**
27-
* Initial image AMD loads for icons. Used to check against and ignore when populating tray context menu.
28-
*/
29-
export const INITIAL_ICON_IMAGE =
30-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAAAXNSR0IArs4c6QAABGJJREFUeF7t1AEJAAAMAsHZv/RyPNwSyDncOQIECEQEFskpJgECBM5geQICBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAgQdWMQCX4yW9owAAAABJRU5ErkJggg==";
31-
32-
export const RECENT_CONVERSATION_TRAY_COUNT = 3;
26+
export {INITIAL_ICON_IMAGE} from "./constants_shared";

src/helpers/constants_shared.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
/**
3+
* Initial image AMD loads for icons. Used to check against and ignore when populating tray context menu.
4+
*/
5+
export const INITIAL_ICON_IMAGE =
6+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAAAXNSR0IArs4c6QAABGJJREFUeF7t1AEJAAAMAsHZv/RyPNwSyDncOQIECEQEFskpJgECBM5geQICBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAAYPlBwgQyAgYrExVghIgYLD8AAECGQGDlalKUAIEDJYfIEAgI2CwMlUJSoCAwfIDBAhkBAxWpipBCRAwWH6AAIGMgMHKVCUoAQIGyw8QIJARMFiZqgQlQMBg+QECBDICBitTlaAECBgsP0CAQEbAYGWqEpQAgQdWMQCX4yW9owAAAABJRU5ErkJggg==";

src/helpers/profileImage.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

src/menu/settingsMenu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BrowserWindow, MenuItem, MenuItemConstructorOptions } from "electron";
1+
import { BaseWindow, MenuItem, MenuItemConstructorOptions } from "electron";
22
import { IS_MAC } from "../helpers/constants";
33
import { settings } from "../helpers/settings";
44
import { separator } from "./items/separator";
@@ -27,7 +27,7 @@ export const settingsMenu: MenuItemConstructorOptions = {
2727
label: "Auto Hide Menu Bar",
2828
type: "checkbox",
2929
checked: autoHideMenuEnabled.value,
30-
click: (item: MenuItem, window?: BrowserWindow): void => {
30+
click: (item: MenuItem, window?: BaseWindow, event?: Electron.KeyboardEvent): void => {
3131
autoHideMenuEnabled.next(item.checked);
3232
window?.setMenuBarVisibility(!autoHideMenuEnabled.value);
3333
window?.setAutoHideMenuBar(autoHideMenuEnabled.value);

src/preload/constants_preload.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Can't use constants.ts in bridge.ts because of context isolation
2+
3+
export const IS_MAC = window.navigator.userAgent.indexOf("Macintosh") > -1;
4+
5+
export const RECENT_CONVERSATION_TRAY_COUNT = 3;
6+
7+
export {INITIAL_ICON_IMAGE} from "../helpers/constants_shared";

0 commit comments

Comments
 (0)