summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-03-09 11:21:14 +0100
committerOskar Nyberg <oskar@mullvad.net>2021-03-10 15:39:31 +0100
commitde735b7ced8bb1148a1f282d975745cfd9d13435 (patch)
treedc74c80de7356f9bcfc58861a23896f3609b3f3d
parent9f8cb65cb0bfbb20793819aa73f12f48259b666a (diff)
downloadmullvadvpn-de735b7ced8bb1148a1f282d975745cfd9d13435.tar.xz
mullvadvpn-de735b7ced8bb1148a1f282d975745cfd9d13435.zip
Return undefined if window or webview is destroyed
-rw-r--r--gui/src/main/index.ts54
-rw-r--r--gui/src/main/window-controller.ts58
-rw-r--r--gui/src/shared/ipc-helpers.ts6
3 files changed, 65 insertions, 53 deletions
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 45a21bf8f6..5d08689036 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -355,7 +355,7 @@ class ApplicationMain {
// closing normally, even programmatically. Therefore re-enable the close button just before
// quitting the app.
// Github issue: https://github.com/electron/electron/issues/15008
- if (process.platform === 'darwin' && this.windowController) {
+ if (process.platform === 'darwin' && this.windowController?.window) {
this.windowController.window.closable = true;
}
@@ -435,9 +435,9 @@ class ApplicationMain {
};
private async initializeWindow() {
- if (this.windowController && this.tray) {
+ if (this.windowController?.window && this.tray) {
this.registerWindowListener(this.windowController);
- this.addContextMenu(this.windowController.window);
+ this.addContextMenu(this.windowController);
if (process.env.NODE_ENV === 'development') {
await this.installDevTools();
@@ -466,18 +466,20 @@ class ApplicationMain {
const filePath = path.resolve(path.join(__dirname, '../renderer/index.html'));
try {
if (process.env.NODE_ENV === 'development') {
- await this.windowController?.window.loadURL(
+ await this.windowController.window.loadURL(
'http://localhost:8080/src/renderer/index.html',
);
} else {
- await this.windowController?.window.loadFile(filePath);
+ await this.windowController.window.loadFile(filePath);
}
} catch (error) {
log.error(`Failed to load index file: ${error.message}`);
}
// disable pinch to zoom
- consumePromise(this.windowController.webContents.setVisualZoomLevelLimits(1, 1));
+ if (this.windowController.webContents) {
+ consumePromise(this.windowController.webContents.setVisualZoomLevelLimits(1, 1));
+ }
}
}
@@ -990,14 +992,14 @@ class ApplicationMain {
}
private registerWindowListener(windowController: WindowController) {
- windowController.window.on('show', () => {
+ windowController.window?.on('show', () => {
// cancel notifications when window appears
this.notificationController.cancelPendingNotifications();
this.updateAccountData();
});
- windowController.window.on('hide', () => {
+ windowController.window?.on('hide', () => {
// ensure notification guard is reset
this.notificationController.resetTunnelStateAnnouncements();
});
@@ -1613,7 +1615,7 @@ class ApplicationMain {
}
}
- private addContextMenu(window: BrowserWindow) {
+ private addContextMenu(windowController: WindowController) {
const menuTemplate: Electron.MenuItemConstructorOptions[] = [
{ role: 'cut' },
{ role: 'copy' },
@@ -1623,15 +1625,15 @@ class ApplicationMain {
];
// add inspect element on right click menu
- window.webContents.on(
+ windowController.window?.webContents.on(
'context-menu',
(_e: Event, props: { x: number; y: number; isEditable: boolean }) => {
const inspectTemplate = [
{
label: 'Inspect element',
click() {
- window.webContents.openDevTools({ mode: 'detach' });
- window.webContents.inspectElement(props.x, props.y);
+ windowController.window?.webContents.openDevTools({ mode: 'detach' });
+ windowController.window?.webContents.inspectElement(props.x, props.y);
},
},
];
@@ -1644,14 +1646,14 @@ class ApplicationMain {
...inspectTemplate,
];
- Menu.buildFromTemplate(inputMenu).popup({ window });
+ Menu.buildFromTemplate(inputMenu).popup({ window: windowController.window });
} else {
- Menu.buildFromTemplate(menuTemplate).popup({ window });
+ Menu.buildFromTemplate(menuTemplate).popup({ window: windowController.window });
}
} else if (process.env.NODE_ENV === 'development') {
// display inspect element for all non-editable
// elements when in development mode
- Menu.buildFromTemplate(inspectTemplate).popup({ window });
+ Menu.buildFromTemplate(inspectTemplate).popup({ window: windowController.window });
}
},
);
@@ -1685,7 +1687,7 @@ class ApplicationMain {
private installWindowsMenubarAppWindowHandlers(tray: Tray, windowController: WindowController) {
if (!this.guiSettings.unpinnedWindow) {
- windowController.window.on('blur', () => {
+ windowController.window?.on('blur', () => {
// Detect if blur happened when user had a cursor above the tray icon.
const trayBounds = tray.getBounds();
const cursorPos = screen.getCursorScreenPoint();
@@ -1709,14 +1711,18 @@ class ApplicationMain {
const { NSEventMonitor, NSEventMask } = require('nseventmonitor');
const macEventMonitor = new NSEventMonitor();
const eventMask = NSEventMask.leftMouseDown | NSEventMask.rightMouseDown;
- const window = windowController.window;
- window.on('show', () => macEventMonitor.start(eventMask, () => windowController.hide()));
- window.on('hide', () => macEventMonitor.stop());
- window.on('blur', () => {
+ windowController.window?.on('show', () =>
+ macEventMonitor.start(eventMask, () => windowController.hide()),
+ );
+ windowController.window?.on('hide', () => macEventMonitor.stop());
+ windowController.window?.on('blur', () => {
// Make sure to hide the menubar window when other program captures the focus.
// But avoid doing that when dev tools capture the focus to make it possible to inspect the UI
- if (window.isVisible() && !window.webContents.isDevToolsFocused()) {
+ if (
+ windowController.window?.isVisible() &&
+ !windowController.window?.webContents.isDevToolsFocused()
+ ) {
windowController.hide();
}
});
@@ -1725,7 +1731,7 @@ class ApplicationMain {
private installWindowCloseHandler(windowController: WindowController) {
if (this.guiSettings.unpinnedWindow) {
- windowController.window.on('close', (closeEvent: Event) => {
+ windowController.window?.on('close', (closeEvent: Event) => {
if (this.quitStage !== AppQuitStage.ready) {
closeEvent.preventDefault();
windowController.hide();
@@ -1735,10 +1741,10 @@ class ApplicationMain {
}
private installGenericFocusHandlers(windowController: WindowController) {
- windowController.window.on('focus', () => {
+ windowController.window?.on('focus', () => {
IpcMainEventChannel.windowFocus.notify(windowController.webContents, true);
});
- windowController.window.on('blur', () => {
+ windowController.window?.on('blur', () => {
IpcMainEventChannel.windowFocus.notify(windowController.webContents, false);
});
}
diff --git a/gui/src/main/window-controller.ts b/gui/src/main/window-controller.ts
index 69ae6acb02..450123d035 100644
--- a/gui/src/main/window-controller.ts
+++ b/gui/src/main/window-controller.ts
@@ -135,12 +135,12 @@ export default class WindowController {
private windowPositioning: IWindowPositioning;
private isWindowReady = false;
- get window(): BrowserWindow {
- return this.windowValue;
+ get window(): BrowserWindow | undefined {
+ return this.windowValue.isDestroyed() ? undefined : this.windowValue;
}
- get webContents(): WebContents {
- return this.webContentsValue;
+ get webContents(): WebContents | undefined {
+ return this.webContentsValue.isDestroyed() ? undefined : this.webContentsValue;
}
constructor(windowValue: BrowserWindow, private tray: Tray, unpinnedWindow: boolean) {
@@ -158,8 +158,8 @@ export default class WindowController {
}
public replaceWindow(window: BrowserWindow, unpinnedWindow: boolean) {
- this.window.removeAllListeners();
- this.window.destroy();
+ this.window?.removeAllListeners();
+ this.window?.destroy();
this.windowValue = window;
this.webContentsValue = window.webContents;
@@ -181,11 +181,11 @@ export default class WindowController {
}
public hide() {
- this.windowValue.hide();
+ this.window?.hide();
}
public toggle() {
- if (this.windowValue.isVisible()) {
+ if (this.window?.isVisible()) {
this.hide();
} else {
this.show();
@@ -193,11 +193,11 @@ export default class WindowController {
}
public isVisible(): boolean {
- return this.windowValue.isVisible();
+ return this.window?.isVisible() ?? false;
}
private showImmediately() {
- const window = this.windowValue;
+ const window = this.window;
// When running with unpinned window on Windows there's a bug that causes the app to become
// wider if opened from minimized if the updated position is set before the window is opened.
@@ -207,8 +207,8 @@ export default class WindowController {
process.platform === 'win32' &&
this.windowPositioning instanceof StandaloneWindowPositioning
) {
- window.show();
- window.focus();
+ window?.show();
+ window?.focus();
this.updatePosition();
this.notifyUpdateWindowShape();
@@ -216,29 +216,35 @@ export default class WindowController {
this.updatePosition();
this.notifyUpdateWindowShape();
- window.show();
- window.focus();
+ window?.show();
+ window?.focus();
}
}
private updatePosition() {
- const { x, y } = this.windowPositioning.getPosition(this.windowValue);
- this.windowValue.setPosition(x, y, false);
+ if (this.window) {
+ const { x, y } = this.windowPositioning.getPosition(this.window);
+ this.window.setPosition(x, y, false);
+ }
}
private notifyUpdateWindowShape() {
- const shapeParameters = this.windowPositioning.getWindowShapeParameters(this.windowValue);
+ if (this.window) {
+ const shapeParameters = this.windowPositioning.getWindowShapeParameters(this.window);
- IpcMainEventChannel.windowShape.notify(this.webContentsValue, shapeParameters);
+ IpcMainEventChannel.windowShape.notify(this.webContentsValue, shapeParameters);
+ }
}
// Installs display event handlers to update the window position on any changes in the display or
// workarea dimensions.
private installDisplayMetricsHandler() {
- screen.addListener('display-metrics-changed', this.onDisplayMetricsChanged);
- this.windowValue.once('closed', () => {
- screen.removeListener('display-metrics-changed', this.onDisplayMetricsChanged);
- });
+ if (this.window) {
+ screen.addListener('display-metrics-changed', this.onDisplayMetricsChanged);
+ this.window.once('closed', () => {
+ screen.removeListener('display-metrics-changed', this.onDisplayMetricsChanged);
+ });
+ }
}
private onDisplayMetricsChanged = (
@@ -246,7 +252,7 @@ export default class WindowController {
_display: Display,
changedMetrics: string[],
) => {
- if (changedMetrics.includes('workArea') && this.windowValue.isVisible()) {
+ if (changedMetrics.includes('workArea') && this.window?.isVisible()) {
this.updatePosition();
this.notifyUpdateWindowShape();
}
@@ -260,11 +266,11 @@ export default class WindowController {
};
private forceResizeWindow() {
- this.windowValue.setSize(this.width, this.height);
+ this.window?.setSize(this.width, this.height);
}
private installWindowReadyHandlers() {
- this.windowValue.once('ready-to-show', () => {
+ this.window?.once('ready-to-show', () => {
this.isWindowReady = true;
});
}
@@ -273,7 +279,7 @@ export default class WindowController {
if (this.isWindowReady) {
closure();
} else {
- this.windowValue.once('ready-to-show', () => {
+ this.window?.once('ready-to-show', () => {
closure();
});
}
diff --git a/gui/src/shared/ipc-helpers.ts b/gui/src/shared/ipc-helpers.ts
index 59eee46459..8c3c851a80 100644
--- a/gui/src/shared/ipc-helpers.ts
+++ b/gui/src/shared/ipc-helpers.ts
@@ -4,7 +4,7 @@ import log from './logging';
type Handler<T, R> = (callback: (arg: T) => R) => void;
type Sender<T, R> = (arg: T) => R;
-type Notifier<T> = (webContents: WebContents, arg: T) => void;
+type Notifier<T> = (webContents: WebContents | undefined, arg: T) => void;
type Listener<T> = (callback: (arg: T) => void) => void;
interface MainToRenderer<T> {
@@ -152,8 +152,8 @@ export function notifyRenderer<T>(): MainToRenderer<T> {
}
function notifyRendererImpl<T>(event: string, _ipcMain: EIpcMain): Notifier<T> {
- return (webContents: WebContents, value: T) => {
- if (webContents.isDestroyed()) {
+ return (webContents, value) => {
+ if (webContents === undefined) {
log.error(`sender(${event}): webContents is already destroyed!`);
} else {
webContents.send(event, value);