diff --git a/e2e/specs/deep_linking/deeplink.test.js b/e2e/specs/deep_linking/deeplink.test.js
index 20155a39..059ae820 100644
--- a/e2e/specs/deep_linking/deeplink.test.js
+++ b/e2e/specs/deep_linking/deeplink.test.js
@@ -43,7 +43,7 @@ describe('application', function desc() {
const browserWindow = await this.app.browserWindow(mainWindow);
const webContentsId = this.serverMap[`${config.teams[1].name}___TAB_MESSAGING`].webContentsId;
const isActive = await browserWindow.evaluate((window, id) => {
- return window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getURL();
+ return window.contentView.children.find((view) => view.webContents.id === id).webContents.getURL();
}, webContentsId);
isActive.should.equal('https://github.com/test/url/');
const dropdownButtonText = await mainWindow.innerText('.ServerDropdownButton');
diff --git a/e2e/specs/menu_bar/dropdown.test.js b/e2e/specs/menu_bar/dropdown.test.js
index 57ac5672..69822e3d 100644
--- a/e2e/specs/menu_bar/dropdown.test.js
+++ b/e2e/specs/menu_bar/dropdown.test.js
@@ -54,17 +54,17 @@ describe('menu_bar/dropdown', function desc() {
after(afterFunc);
it('MM-T4406_1 should show the dropdown', async () => {
- let dropdownHeight = await browserWindow.evaluate((window) => window.getBrowserViews().find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
+ let dropdownHeight = await browserWindow.evaluate((window) => window.contentView.children.find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
dropdownHeight.should.equal(0);
await mainWindow.click('.ServerDropdownButton');
- dropdownHeight = await browserWindow.evaluate((window) => window.getBrowserViews().find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
+ dropdownHeight = await browserWindow.evaluate((window) => window.contentView.children.find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
dropdownHeight.should.be.greaterThan(0);
});
it('MM-T4406_2 should hide the dropdown', async () => {
await mainWindow.click('.TabBar');
- const dropdownHeight = await browserWindow.evaluate((window) => window.getBrowserViews().find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
+ const dropdownHeight = await browserWindow.evaluate((window) => window.contentView.children.find((view) => view.webContents.getURL().includes('dropdown')).getBounds().height);
dropdownHeight.should.equal(0);
});
});
@@ -100,9 +100,9 @@ describe('menu_bar/dropdown', function desc() {
after(afterFunc);
it('MM-T4408_1 should show the first view', async () => {
- const firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.exampleURL);
+ const firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.contentView.children.find((view) => view.webContents.getURL() === url)), env.exampleURL);
firstViewIsAttached.should.be.true;
- const secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/')));
+ const secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.contentView.children.find((view) => view.webContents.getURL() === 'https://github.com/')));
secondViewIsAttached.should.be.false;
});
@@ -110,9 +110,9 @@ describe('menu_bar/dropdown', function desc() {
await mainWindow.click('.ServerDropdownButton');
await dropdownView.click('.ServerDropdown button.ServerDropdown__button:nth-child(2)');
- const firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.exampleURL);
+ const firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.contentView.children.find((view) => view.webContents.getURL() === url)), env.exampleURL);
firstViewIsAttached.should.be.false;
- const secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/')));
+ const secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.contentView.children.find((view) => view.webContents.getURL() === 'https://github.com/')));
secondViewIsAttached.should.be.true;
});
});
diff --git a/e2e/specs/menu_bar/view_menu.test.js b/e2e/specs/menu_bar/view_menu.test.js
index 5afebf47..10f0681c 100644
--- a/e2e/specs/menu_bar/view_menu.test.js
+++ b/e2e/specs/menu_bar/view_menu.test.js
@@ -11,7 +11,7 @@ const {asyncSleep} = require('../../modules/utils');
async function setupPromise(window, id) {
const promise = new Promise((resolve) => {
- const browserView = window.getBrowserViews().find((view) => view.webContents.id === id);
+ const browserView = window.contentView.children.find((view) => view.webContents.id === id);
browserView.webContents.on('did-finish-load', () => {
resolve();
});
@@ -22,13 +22,13 @@ async function setupPromise(window, id) {
function getZoomFactorOfServer(browserWindow, serverId) {
return browserWindow.evaluate(
- (window, id) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getZoomFactor(),
+ (window, id) => window.contentView.children.find((view) => view.webContents.id === id).webContents.getZoomFactor(),
serverId,
);
}
function setZoomFactorOfServer(browserWindow, serverId, zoomFactor) {
return browserWindow.evaluate(
- (window, {id, zoom}) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.setZoomFactor(zoom),
+ (window, {id, zoom}) => window.contentView.children.find((view) => view.webContents.id === id).webContents.setZoomFactor(zoom),
{id: serverId, zoom: zoomFactor},
);
}
@@ -82,12 +82,12 @@ describe('menu/view', function desc() {
robot.keyTap('=', [env.cmdOrCtrl]);
await asyncSleep(1000);
- let zoomLevel = await browserWindow.evaluate((window, id) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
+ let zoomLevel = await browserWindow.evaluate((window, id) => window.contentView.children.find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
zoomLevel.should.be.greaterThan(1);
robot.keyTap('0', [env.cmdOrCtrl]);
await asyncSleep(1000);
- zoomLevel = await browserWindow.evaluate((window, id) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
+ zoomLevel = await browserWindow.evaluate((window, id) => window.contentView.children.find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
zoomLevel.should.be.equal(1);
});
@@ -104,7 +104,7 @@ describe('menu/view', function desc() {
robot.keyTap('=', [env.cmdOrCtrl]);
await asyncSleep(1000);
- const zoomLevel = await browserWindow.evaluate((window, id) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
+ const zoomLevel = await browserWindow.evaluate((window, id) => window.contentView.children.find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
zoomLevel.should.be.greaterThan(1);
});
@@ -144,7 +144,7 @@ describe('menu/view', function desc() {
robot.keyTap('-', [env.cmdOrCtrl]);
await asyncSleep(1000);
- const zoomLevel = await browserWindow.evaluate((window, id) => window.getBrowserViews().find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
+ const zoomLevel = await browserWindow.evaluate((window, id) => window.contentView.children.find((view) => view.webContents.id === id).webContents.getZoomFactor(), firstServerId);
zoomLevel.should.be.lessThan(1);
});
diff --git a/i18n/en.json b/i18n/en.json
index 2172facd..cf4a77d1 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -159,9 +159,9 @@
"renderer.components.errorView.cannotConnectToAppName": "Cannot connect to {appName}",
"renderer.components.errorView.havingTroubleConnecting": "We're having trouble connecting to {appName}. We'll continue to try and establish a connection.",
"renderer.components.errorView.refreshThenVerify": "If refreshing this page (Ctrl+R or Command+R) does not work please verify that:",
- "renderer.components.errorView.troubleshooting.browserView.canReachFromBrowserWindow": "You can reach {url} from a browser window.",
"renderer.components.errorView.troubleshooting.computerIsConnected": "Your computer is connected to the internet.",
"renderer.components.errorView.troubleshooting.urlIsCorrect.appNameIsCorrect": "The {appName} URL {url} is correct",
+ "renderer.components.errorView.troubleshooting.webContentsView.canReachFromBrowserWindow": "You can reach {url} from a browser window.",
"renderer.components.input.required": "This field is required",
"renderer.components.mainPage.contextMenu.ariaLabel": "Context menu",
"renderer.components.mainPage.titleBar": "{appName}",
diff --git a/package-lock.json b/package-lock.json
index b7c4cdd1..21af7837 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,7 +16,7 @@
"bootstrap": "4.6.1",
"bootstrap-dark": "1.0.3",
"classnames": "2.5.1",
- "electron-context-menu": "3.6.1",
+ "electron-context-menu": "4.0.4",
"electron-extension-installer": "1.2.0",
"electron-is-dev": "2.0.0",
"electron-log": "5.2.0",
@@ -5281,6 +5281,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "optional": true,
"engines": {
"node": ">=8"
}
@@ -6226,6 +6228,8 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"slice-ansi": "^3.0.0",
"string-width": "^4.2.0"
@@ -7443,26 +7447,136 @@
"dev": true
},
"node_modules/electron-context-menu": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-3.6.1.tgz",
- "integrity": "sha512-lcpO6tzzKUROeirhzBjdBWNqayEThmdW+2I2s6H6QMrwqTVyT3EK47jW3Nxm60KTxl5/bWfEoIruoUNn57/QkQ==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-4.0.4.tgz",
+ "integrity": "sha512-XPGj35npL8+MG9Lx5ukmK/h8KLmjYJ3e1GvwWKrNZvf2ocv746WXIyltoV1yWtkEPT7g2kQ8hFmu0ZupK5KieA==",
"dependencies": {
- "cli-truncate": "^2.1.0",
- "electron-dl": "^3.2.1",
- "electron-is-dev": "^2.0.0"
+ "cli-truncate": "^4.0.0",
+ "electron-dl": "^4.0.0",
+ "electron-is-dev": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/electron-context-menu/node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/electron-is-dev": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-3.0.1.tgz",
+ "integrity": "sha512-8TjjAh8Ec51hUi3o4TaU0mD3GMTOESi866oRNavj9A3IQJ7pmv+MJVmdZBFGw4GFT36X7bkqnuDNYvkQgvyI8Q==",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/electron-context-menu/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/electron-dl": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.5.0.tgz",
- "integrity": "sha512-Oj+VSuScVx8hEKM2HEvTQswTX6G3MLh7UoAz/oZuvKyNDfudNi1zY6PK/UnFoK1nCl9DF6k+3PFwElKbtZlDig==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-4.0.0.tgz",
+ "integrity": "sha512-USiB9816d2JzKv0LiSbreRfTg5lDk3lWh0vlx/gugCO92ZIJkHVH0UM18EHvKeadErP6Xn4yiTphWzYfbA2Ong==",
"dependencies": {
"ext-name": "^5.0.0",
- "pupa": "^2.0.1",
- "unused-filename": "^2.1.0"
+ "pupa": "^3.1.0",
+ "unused-filename": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -7655,8 +7769,7 @@
"node_modules/emoji-regex": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
- "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
- "dev": true
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
},
"node_modules/emojis-list": {
"version": "3.0.0",
@@ -7914,11 +8027,14 @@
}
},
"node_modules/escape-goat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
- "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escape-string-regexp": {
@@ -9288,6 +9404,17 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@@ -13296,14 +13423,6 @@
"node": ">=12"
}
},
- "node_modules/modify-filename": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz",
- "integrity": "sha512-EickqnKq3kVVaZisYuCxhtKbZjInCuwgwZWyAmRIp1NTMhri7r3380/uqwrUHfaDiPzLVTuoNy4whX66bxPVog==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@@ -14008,6 +14127,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -14535,14 +14655,17 @@
}
},
"node_modules/pupa": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz",
- "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz",
+ "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==",
"dependencies": {
- "escape-goat": "^2.0.0"
+ "escape-goat": "^4.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pure-rand": {
@@ -15644,6 +15767,8 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
@@ -15657,6 +15782,8 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15671,6 +15798,8 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "optional": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15681,7 +15810,9 @@
"node_modules/slice-ansi/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "optional": true
},
"node_modules/smart-buffer": {
"version": "4.2.0",
@@ -16806,15 +16937,37 @@
}
},
"node_modules/unused-filename": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz",
- "integrity": "sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-4.0.1.tgz",
+ "integrity": "sha512-ZX6U1J04K1FoSUeoX1OicAhw4d0aro2qo+L8RhJkiGTNtBNkd/Fi1Wxoc9HzcVu6HfOzm0si/N15JjxFmD1z6A==",
"dependencies": {
- "modify-filename": "^1.1.0",
- "path-exists": "^4.0.0"
+ "escape-string-regexp": "^5.0.0",
+ "path-exists": "^5.0.0"
},
"engines": {
- "node": ">=8"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unused-filename/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unused-filename/node_modules/path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/update-browserslist-db": {
diff --git a/package.json b/package.json
index eb925f69..3b22dac6 100644
--- a/package.json
+++ b/package.json
@@ -165,7 +165,7 @@
"bootstrap": "4.6.1",
"bootstrap-dark": "1.0.3",
"classnames": "2.5.1",
- "electron-context-menu": "3.6.1",
+ "electron-context-menu": "4.0.4",
"electron-extension-installer": "1.2.0",
"electron-is-dev": "2.0.0",
"electron-log": "5.2.0",
diff --git a/src/common/communication.ts b/src/common/communication.ts
index cd73b89d..5d41cc72 100644
--- a/src/common/communication.ts
+++ b/src/common/communication.ts
@@ -108,8 +108,6 @@ export const PING_DOMAIN = 'ping-domain';
export const GET_LANGUAGE_INFORMATION = 'get-language-information';
export const GET_AVAILABLE_LANGUAGES = 'get-available-languages';
-export const VIEW_FINISHED_RESIZING = 'view-finished-resizing';
-
// Calls
export const GET_DESKTOP_SOURCES = 'get-desktop-sources';
export const DESKTOP_SOURCES_MODAL_REQUEST = 'desktop-sources-modal-request';
diff --git a/src/common/utils/constants.ts b/src/common/utils/constants.ts
index 6b347784..ee710483 100644
--- a/src/common/utils/constants.ts
+++ b/src/common/utils/constants.ts
@@ -36,7 +36,7 @@ export const DOWNLOADS_DROPDOWN_MENU_HEIGHT = 160;
export const DOWNLOADS_DROPDOWN_MENU_WIDTH = 154;
export const DOWNLOADS_DROPDOWN_MENU_PADDING = 12;
-// In order to display the box-shadow & radius on the left + right, use this WIDTH in the browserView for downloadsDropdown
+// In order to display the box-shadow & radius on the left + right, use this WIDTH in the webContentsView for downloadsDropdown
export const DOWNLOADS_DROPDOWN_FULL_WIDTH = DOWNLOADS_DROPDOWN_PADDING + DOWNLOADS_DROPDOWN_WIDTH + TAB_BAR_PADDING;
export const DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH = (DOWNLOADS_DROPDOWN_MENU_PADDING * 2) + DOWNLOADS_DROPDOWN_MENU_WIDTH;
export const DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT = DOWNLOADS_DROPDOWN_MENU_HEIGHT + TAB_BAR_PADDING; // only bottom padding included for better positioning
diff --git a/src/main/app/app.test.js b/src/main/app/app.test.js
index 6122dfdf..6682ed44 100644
--- a/src/main/app/app.test.js
+++ b/src/main/app/app.test.js
@@ -162,7 +162,7 @@ describe('main/app/app', () => {
expect(CertificateStore.save).toHaveBeenCalled();
});
- it('should load URL using MattermostBrowserView when trusting certificate', async () => {
+ it('should load URL using MattermostWebContentsView when trusting certificate', async () => {
dialog.showMessageBox.mockResolvedValue({response: 0});
await handleAppCertificateError(event, webContents, testURL, 'error-1', certificate, callback);
expect(callback).toHaveBeenCalledWith(true);
diff --git a/src/main/contextMenu.test.js b/src/main/contextMenu.test.js
index 15d73d66..c2a4317d 100644
--- a/src/main/contextMenu.test.js
+++ b/src/main/contextMenu.test.js
@@ -10,7 +10,7 @@ jest.mock('electron-context-menu', () => {
describe('main/contextMenu', () => {
describe('shouldShowMenu', () => {
- const contextMenu = new ContextMenu();
+ const contextMenu = new ContextMenu({}, {webContents: {}});
it('should not show menu on internal link', () => {
expect(contextMenu.menuOptions.shouldShowMenu(null, {
@@ -73,7 +73,7 @@ describe('main/contextMenu', () => {
describe('reload', () => {
it('should call dispose on reload', () => {
- const contextMenu = new ContextMenu();
+ const contextMenu = new ContextMenu({}, {webContents: {}});
const fn = contextMenu.menuDispose;
contextMenu.reload();
expect(fn).toHaveBeenCalled();
diff --git a/src/main/contextMenu.ts b/src/main/contextMenu.ts
index 1429cf52..31a663ac 100644
--- a/src/main/contextMenu.ts
+++ b/src/main/contextMenu.ts
@@ -2,7 +2,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import type {BrowserView, BrowserWindow, ContextMenuParams, Event} from 'electron';
+import type {WebContentsView, BrowserWindow, ContextMenuParams, Event} from 'electron';
import type {Options} from 'electron-context-menu';
import electronContextMenu from 'electron-context-menu';
@@ -29,11 +29,11 @@ const defaultMenuOptions = {
};
export default class ContextMenu {
- view: BrowserWindow | BrowserView;
+ view: BrowserWindow | WebContentsView;
menuOptions: Options;
menuDispose?: () => void;
- constructor(options: Options, view: BrowserWindow | BrowserView) {
+ constructor(options: Options, view: BrowserWindow | WebContentsView) {
const providedOptions: Options = options || {};
this.menuOptions = Object.assign({}, defaultMenuOptions, providedOptions);
@@ -52,7 +52,7 @@ export default class ContextMenu {
reload = () => {
this.dispose();
- const options = {window: this.view, ...this.menuOptions};
+ const options = {window: this.view.webContents, ...this.menuOptions};
this.menuDispose = electronContextMenu(options);
};
}
diff --git a/src/main/diagnostics/steps/internal/utils.test.js b/src/main/diagnostics/steps/internal/utils.test.js
index 1796625d..ad90219f 100644
--- a/src/main/diagnostics/steps/internal/utils.test.js
+++ b/src/main/diagnostics/steps/internal/utils.test.js
@@ -91,14 +91,16 @@ describe('main/diagnostics/utils', () => {
isDestroyed: () => false,
isVisible: () => true,
isEnabled: () => true,
- getBrowserViews: () => [{
- getBounds: () => ({
- x: 0,
- y: 0,
- width: 800,
- height: 500,
- }),
- }],
+ contentView: {
+ children: [{
+ getBounds: () => ({
+ x: 0,
+ y: 0,
+ width: 800,
+ height: 500,
+ }),
+ }],
+ },
};
it('should return true if window ok', () => {
expect(browserWindowVisibilityStatus('testWindow', bWindow).every((check) => check.ok)).toBe(true);
@@ -118,17 +120,19 @@ describe('main/diagnostics/utils', () => {
it('should return false if window is not enabled', () => {
expect(browserWindowVisibilityStatus('testWindow', {...bWindow, isEnabled: () => false}).every((check) => check.ok)).toBe(false);
});
- it('should return false if a child browserView has invalid bounds', () => {
+ it('should return false if a child webContentsView has invalid bounds', () => {
expect(browserWindowVisibilityStatus('testWindow', {
...bWindow,
- getBrowserViews: () => [{
- getBounds: () => ({
- x: -1,
- y: -4000,
- width: 800,
- height: 500,
- }),
- }],
+ contentView: {
+ children: [{
+ getBounds: () => ({
+ x: -1,
+ y: -4000,
+ width: 800,
+ height: 500,
+ }),
+ }],
+ },
}).every((check) => check.ok)).toBe(false);
});
});
diff --git a/src/main/diagnostics/steps/internal/utils.ts b/src/main/diagnostics/steps/internal/utils.ts
index afb2ccb6..ef2f1322 100644
--- a/src/main/diagnostics/steps/internal/utils.ts
+++ b/src/main/diagnostics/steps/internal/utils.ts
@@ -109,7 +109,7 @@ export function browserWindowVisibilityStatus(name: string, bWindow?: BrowserWin
const destroyed = bWindow.isDestroyed();
const visible = bWindow.isVisible();
const enabled = bWindow.isEnabled();
- const browserViewsBounds = bWindow.getBrowserViews()?.map((view) => view.getBounds());
+ const webContentsViewsBounds = bWindow.contentView.children.map((view) => view.getBounds());
status.push({
name: 'windowExists',
@@ -141,9 +141,9 @@ export function browserWindowVisibilityStatus(name: string, bWindow?: BrowserWin
ok: enabled,
});
status.push({
- name: 'browserViewsBounds',
- ok: browserViewsBounds.every((bounds) => boundsOk(bounds)),
- data: browserViewsBounds,
+ name: 'webContentsViewsBounds',
+ ok: webContentsViewsBounds.every((bounds) => boundsOk(bounds)),
+ data: webContentsViewsBounds,
});
return status;
diff --git a/src/main/downloadsManager.test.js b/src/main/downloadsManager.test.js
index 8b9cfe1a..4cb57307 100644
--- a/src/main/downloadsManager.test.js
+++ b/src/main/downloadsManager.test.js
@@ -30,7 +30,7 @@ jest.mock('electron', () => {
getAppPath: jest.fn(),
getPath: jest.fn(() => '/valid/downloads/path'),
},
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
webContents: {
loadURL: jest.fn(),
focus: jest.fn(),
diff --git a/src/main/preload/externalAPI.ts b/src/main/preload/externalAPI.ts
index f8016bd8..6e2f9843 100644
--- a/src/main/preload/externalAPI.ts
+++ b/src/main/preload/externalAPI.ts
@@ -13,7 +13,6 @@ import {
USER_ACTIVITY_UPDATE,
BROWSER_HISTORY_PUSH,
GET_VIEW_INFO_FOR_TEST,
- VIEW_FINISHED_RESIZING,
CALLS_JOIN_CALL,
CALLS_JOINED_CALL,
CALLS_LEAVE_CALL,
@@ -140,12 +139,6 @@ if (process.env.NODE_ENV === 'test') {
****************************************************************************
*/
-// Let the main process know when the window has finished resizing
-// This is to reduce the amount of white box that happens when expand the BrowserView
-window.addEventListener('resize', () => {
- ipcRenderer.send(VIEW_FINISHED_RESIZING);
-});
-
// Enable secure input on macOS clients when the user is on a password input
let isPasswordBox = false;
const shouldSecureInput = (element: {tagName?: string; type?: string} | null, force = false) => {
diff --git a/src/main/preload/internalAPI.js b/src/main/preload/internalAPI.js
index d5bb4ad3..caf1fe21 100644
--- a/src/main/preload/internalAPI.js
+++ b/src/main/preload/internalAPI.js
@@ -89,7 +89,6 @@ import {
OPEN_WINDOWS_CAMERA_PREFERENCES,
OPEN_WINDOWS_MICROPHONE_PREFERENCES,
GET_MEDIA_ACCESS_STATUS,
- VIEW_FINISHED_RESIZING,
GET_NONCE,
IS_DEVELOPER_MODE_ENABLED,
METRICS_REQUEST,
@@ -177,7 +176,6 @@ contextBridge.exposeInMainWorld('desktop', {
openWindowsCameraPreferences: () => ipcRenderer.send(OPEN_WINDOWS_CAMERA_PREFERENCES),
openWindowsMicrophonePreferences: () => ipcRenderer.send(OPEN_WINDOWS_MICROPHONE_PREFERENCES),
getMediaAccessStatus: (mediaType) => ipcRenderer.invoke(GET_MEDIA_ACCESS_STATUS, mediaType),
- viewFinishedResizing: () => ipcRenderer.send(VIEW_FINISHED_RESIZING),
downloadsDropdown: {
toggleDownloadsDropdownMenu: (payload) => ipcRenderer.send(TOGGLE_DOWNLOADS_DROPDOWN_MENU, payload),
diff --git a/src/main/views/MattermostBrowserView.test.js b/src/main/views/MattermostWebContentsView.test.js
similarity index 72%
rename from src/main/views/MattermostBrowserView.test.js
rename to src/main/views/MattermostWebContentsView.test.js
index ec01e09f..b0e78971 100644
--- a/src/main/views/MattermostBrowserView.test.js
+++ b/src/main/views/MattermostWebContentsView.test.js
@@ -8,7 +8,7 @@ import {LOAD_FAILED, UPDATE_TARGET_URL} from 'common/communication';
import {MattermostServer} from 'common/servers/MattermostServer';
import MessagingView from 'common/views/MessagingView';
-import {MattermostBrowserView} from './MattermostBrowserView';
+import {MattermostWebContentsView} from './MattermostWebContentsView';
import ContextMenu from '../contextMenu';
import MainWindow from '../windows/mainWindow';
@@ -18,7 +18,7 @@ jest.mock('electron', () => ({
getVersion: () => '5.0.0',
getPath: jest.fn(() => '/valid/downloads/path'),
},
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
webContents: {
loadURL: jest.fn(),
on: jest.fn(),
@@ -72,10 +72,10 @@ jest.mock('main/performanceMonitor', () => ({
const server = new MattermostServer({name: 'server_name', url: 'http://server-1.com'});
const view = new MessagingView(server, true);
-describe('main/views/MattermostBrowserView', () => {
+describe('main/views/MattermostWebContentsView', () => {
describe('load', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
MainWindow.get.mockReturnValue(window);
@@ -85,38 +85,38 @@ describe('main/views/MattermostBrowserView', () => {
it('should load provided URL when provided', async () => {
const promise = Promise.resolve();
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.load('http://server-2.com');
await promise;
- expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-2.com/', expect.any(Object));
+ expect(mattermostView.webContentsView.webContents.loadURL).toBeCalledWith('http://server-2.com/', expect.any(Object));
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-2.com/');
});
it('should load server URL when not provided', async () => {
const promise = Promise.resolve();
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.load();
await promise;
- expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
+ expect(mattermostView.webContentsView.webContents.loadURL).toBeCalledWith('http://server-1.com/', expect.any(Object));
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-1.com/');
});
it('should load server URL when bad url provided', async () => {
const promise = Promise.resolve();
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.load('a-bad {
const error = new Error('test');
const promise = Promise.reject(error);
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.load('a-bad {
const error = new Error('test');
error.code = 'ERR_CERT_ERROR';
const promise = Promise.reject(error);
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.load('a-bad {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
const retryInBackgroundFn = jest.fn();
beforeEach(() => {
jest.useFakeTimers();
MainWindow.get.mockReturnValue(window);
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => Promise.resolve());
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => Promise.resolve());
mattermostView.loadSuccess = jest.fn();
mattermostView.loadRetry = jest.fn();
mattermostView.emit = jest.fn();
@@ -154,16 +154,16 @@ describe('main/views/MattermostBrowserView', () => {
});
it('should do nothing when webcontents are destroyed', () => {
- const webContents = mattermostView.browserView.webContents;
- mattermostView.browserView.webContents = null;
+ const webContents = mattermostView.webContentsView.webContents;
+ mattermostView.webContentsView.webContents = null;
mattermostView.retry('http://server-1.com')();
expect(mattermostView.loadSuccess).not.toBeCalled();
- mattermostView.browserView.webContents = webContents;
+ mattermostView.webContentsView.webContents = webContents;
});
it('should call loadSuccess on successful load', async () => {
const promise = Promise.resolve();
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.retry('http://server-1.com')();
await promise;
expect(mattermostView.loadSuccess).toBeCalledWith('http://server-1.com');
@@ -173,10 +173,10 @@ describe('main/views/MattermostBrowserView', () => {
mattermostView.maxRetries = 10;
const error = new Error('test');
const promise = Promise.reject(error);
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.retry('http://server-1.com')();
await expect(promise).rejects.toThrow(error);
- expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
+ expect(mattermostView.webContentsView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
expect(mattermostView.loadRetry).toBeCalledWith('http://server-1.com', error);
});
@@ -184,10 +184,10 @@ describe('main/views/MattermostBrowserView', () => {
mattermostView.maxRetries = 0;
const error = new Error('test');
const promise = Promise.reject(error);
- mattermostView.browserView.webContents.loadURL.mockImplementation(() => promise);
+ mattermostView.webContentsView.webContents.loadURL.mockImplementation(() => promise);
mattermostView.retry('http://server-1.com')();
await expect(promise).rejects.toThrow(error);
- expect(mattermostView.browserView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
+ expect(mattermostView.webContentsView.webContents.loadURL).toBeCalledWith('http://server-1.com', expect.any(Object));
expect(mattermostView.loadRetry).not.toBeCalled();
expect(MainWindow.sendToRenderer).toBeCalledWith(LOAD_FAILED, mattermostView.view.id, expect.any(String), expect.any(String));
expect(mattermostView.status).toBe(-1);
@@ -198,7 +198,7 @@ describe('main/views/MattermostBrowserView', () => {
describe('goToOffset', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
mattermostView.reload = jest.fn();
afterEach(() => {
@@ -207,18 +207,18 @@ describe('main/views/MattermostBrowserView', () => {
});
it('should only go to offset if it can', () => {
- mattermostView.browserView.webContents.navigationHistory.canGoToOffset.mockReturnValue(false);
+ mattermostView.webContentsView.webContents.navigationHistory.canGoToOffset.mockReturnValue(false);
mattermostView.goToOffset(1);
- expect(mattermostView.browserView.webContents.navigationHistory.goToOffset).not.toBeCalled();
+ expect(mattermostView.webContentsView.webContents.navigationHistory.goToOffset).not.toBeCalled();
- mattermostView.browserView.webContents.navigationHistory.canGoToOffset.mockReturnValue(true);
+ mattermostView.webContentsView.webContents.navigationHistory.canGoToOffset.mockReturnValue(true);
mattermostView.goToOffset(1);
- expect(mattermostView.browserView.webContents.navigationHistory.goToOffset).toBeCalled();
+ expect(mattermostView.webContentsView.webContents.navigationHistory.goToOffset).toBeCalled();
});
it('should call reload if an error occurs', () => {
- mattermostView.browserView.webContents.navigationHistory.canGoToOffset.mockReturnValue(true);
- mattermostView.browserView.webContents.navigationHistory.goToOffset.mockImplementation(() => {
+ mattermostView.webContentsView.webContents.navigationHistory.canGoToOffset.mockReturnValue(true);
+ mattermostView.webContentsView.webContents.navigationHistory.goToOffset.mockImplementation(() => {
throw new Error('hi');
});
mattermostView.goToOffset(1);
@@ -228,8 +228,8 @@ describe('main/views/MattermostBrowserView', () => {
describe('onLogin', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
- mattermostView.browserView.webContents.getURL = jest.fn();
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
+ mattermostView.webContentsView.webContents.getURL = jest.fn();
mattermostView.reload = jest.fn();
afterEach(() => {
@@ -238,19 +238,19 @@ describe('main/views/MattermostBrowserView', () => {
});
it('should reload view when URL is not on subpath of original server URL', () => {
- mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-2.com/subpath');
+ mattermostView.webContentsView.webContents.getURL.mockReturnValue('http://server-2.com/subpath');
mattermostView.onLogin(true);
expect(mattermostView.reload).toHaveBeenCalled();
});
it('should not reload if URLs are matching', () => {
- mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-1.com');
+ mattermostView.webContentsView.webContents.getURL.mockReturnValue('http://server-1.com');
mattermostView.onLogin(true);
expect(mattermostView.reload).not.toHaveBeenCalled();
});
it('should not reload if URL is subpath of server URL', () => {
- mattermostView.browserView.webContents.getURL.mockReturnValue('http://server-1.com/subpath');
+ mattermostView.webContentsView.webContents.getURL.mockReturnValue('http://server-1.com/subpath');
mattermostView.onLogin(true);
expect(mattermostView.reload).not.toHaveBeenCalled();
});
@@ -258,7 +258,7 @@ describe('main/views/MattermostBrowserView', () => {
describe('loadSuccess', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
jest.useFakeTimers();
@@ -285,8 +285,14 @@ describe('main/views/MattermostBrowserView', () => {
});
describe('show', () => {
- const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn(), on: jest.fn(), setTopBrowserView: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const window = {
+ contentView: {
+ addChildView: jest.fn(),
+ removeChildView: jest.fn(),
+ },
+ on: jest.fn(),
+ };
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
jest.useFakeTimers();
@@ -304,7 +310,7 @@ describe('main/views/MattermostBrowserView', () => {
it('should add browser view to window and set bounds when request is true and view not currently visible', () => {
mattermostView.isVisible = false;
mattermostView.show();
- expect(window.addBrowserView).toBeCalledWith(mattermostView.browserView);
+ expect(window.contentView.addChildView).toBeCalledWith(mattermostView.webContentsView);
expect(mattermostView.setBounds).toBeCalled();
expect(mattermostView.isVisible).toBe(true);
});
@@ -312,7 +318,7 @@ describe('main/views/MattermostBrowserView', () => {
it('should do nothing when not toggling', () => {
mattermostView.isVisible = true;
mattermostView.show();
- expect(window.addBrowserView).not.toBeCalled();
+ expect(window.contentView.addChildView).not.toBeCalled();
});
it('should focus view if view is ready', () => {
@@ -324,8 +330,14 @@ describe('main/views/MattermostBrowserView', () => {
});
describe('hide', () => {
- const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn(), on: jest.fn(), setTopBrowserView: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const window = {
+ contentView: {
+ addChildView: jest.fn(),
+ removeChildView: jest.fn(),
+ },
+ on: jest.fn(),
+ };
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
MainWindow.get.mockReturnValue(window);
@@ -334,20 +346,20 @@ describe('main/views/MattermostBrowserView', () => {
it('should remove browser view', () => {
mattermostView.isVisible = true;
mattermostView.hide();
- expect(window.removeBrowserView).toBeCalledWith(mattermostView.browserView);
+ expect(window.contentView.removeChildView).toBeCalledWith(mattermostView.webContentsView);
expect(mattermostView.isVisible).toBe(false);
});
it('should do nothing when not toggling', () => {
mattermostView.isVisible = false;
mattermostView.hide();
- expect(window.removeBrowserView).not.toBeCalled();
+ expect(window.contentView.removeChildView).not.toBeCalled();
});
});
describe('updateHistoryButton', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
MainWindow.get.mockReturnValue(window);
@@ -356,13 +368,13 @@ describe('main/views/MattermostBrowserView', () => {
it('should erase history and set isAtRoot when navigating to root URL', () => {
mattermostView.atRoot = false;
mattermostView.updateHistoryButton();
- expect(mattermostView.browserView.webContents.navigationHistory.clear).toHaveBeenCalled();
+ expect(mattermostView.webContentsView.webContents.navigationHistory.clear).toHaveBeenCalled();
expect(mattermostView.isAtRoot).toBe(true);
});
});
describe('destroy', () => {
- const window = {removeBrowserView: jest.fn(), on: jest.fn()};
+ const window = {contentView: {removeChildView: jest.fn()}, on: jest.fn()};
const contextMenu = {
dispose: jest.fn(),
};
@@ -373,22 +385,22 @@ describe('main/views/MattermostBrowserView', () => {
});
it('should remove browser view from window', () => {
- const mattermostView = new MattermostBrowserView(view, {}, {});
- mattermostView.browserView.webContents.close = jest.fn();
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
+ mattermostView.webContentsView.webContents.close = jest.fn();
mattermostView.destroy();
- expect(window.removeBrowserView).toBeCalledWith(mattermostView.browserView);
+ expect(window.contentView.removeChildView).toBeCalledWith(mattermostView.webContentsView);
});
it('should clear mentions', () => {
- const mattermostView = new MattermostBrowserView(view, {}, {});
- mattermostView.browserView.webContents.close = jest.fn();
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
+ mattermostView.webContentsView.webContents.close = jest.fn();
mattermostView.destroy();
expect(AppState.clear).toBeCalledWith(mattermostView.view.id);
});
it('should clear outstanding timeouts', () => {
- const mattermostView = new MattermostBrowserView(view, {}, {});
- mattermostView.browserView.webContents.close = jest.fn();
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
+ mattermostView.webContentsView.webContents.close = jest.fn();
const spy = jest.spyOn(global, 'clearTimeout');
mattermostView.retryLoad = 999;
mattermostView.removeLoading = 1000;
@@ -399,7 +411,7 @@ describe('main/views/MattermostBrowserView', () => {
describe('handleInputEvents', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
it('should open three dot menu on pressing Alt', () => {
MainWindow.get.mockReturnValue(window);
@@ -424,7 +436,7 @@ describe('main/views/MattermostBrowserView', () => {
describe('handleUpdateTarget', () => {
const window = {on: jest.fn()};
- const mattermostView = new MattermostBrowserView(view, {}, {});
+ const mattermostView = new MattermostWebContentsView(view, {}, {});
beforeEach(() => {
MainWindow.get.mockReturnValue(window);
diff --git a/src/main/views/MattermostBrowserView.ts b/src/main/views/MattermostWebContentsView.ts
similarity index 79%
rename from src/main/views/MattermostBrowserView.ts
rename to src/main/views/MattermostWebContentsView.ts
index 1a5599cd..27a71824 100644
--- a/src/main/views/MattermostBrowserView.ts
+++ b/src/main/views/MattermostWebContentsView.ts
@@ -1,8 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {BrowserView, app, ipcMain} from 'electron';
-import type {BrowserViewConstructorOptions, Event, Input} from 'electron/main';
+import {WebContentsView, app, ipcMain} from 'electron';
+import type {WebContentsViewConstructorOptions, Event, Input} from 'electron/main';
import {EventEmitter} from 'events';
import AppState from 'common/appState';
@@ -38,16 +38,15 @@ enum Status {
WAITING_MM,
ERROR = -1,
}
-
-export class MattermostBrowserView extends EventEmitter {
+export class MattermostWebContentsView extends EventEmitter {
view: MattermostView;
isVisible: boolean;
private log: Logger;
- private browserView: BrowserView;
+ private webContentsView: WebContentsView;
private loggedIn: boolean;
private atRoot: boolean;
- private options: BrowserViewConstructorOptions;
+ private options: WebContentsViewConstructorOptions;
private removeLoading?: NodeJS.Timeout;
private contextMenu?: ContextMenu;
private status?: Status;
@@ -55,7 +54,7 @@ export class MattermostBrowserView extends EventEmitter {
private maxRetries: number;
private altPressStatus: boolean;
- constructor(view: MattermostView, options: BrowserViewConstructorOptions) {
+ constructor(view: MattermostView, options: WebContentsViewConstructorOptions) {
super();
this.view = view;
@@ -72,29 +71,28 @@ export class MattermostBrowserView extends EventEmitter {
this.isVisible = false;
this.loggedIn = false;
this.atRoot = true;
- this.browserView = new BrowserView(this.options);
+ this.webContentsView = new WebContentsView(this.options);
this.resetLoadingStatus();
- this.log = ServerManager.getViewLog(this.id, 'MattermostBrowserView');
+ this.log = ServerManager.getViewLog(this.id, 'MattermostWebContentsView');
this.log.verbose('View created');
- this.browserView.webContents.on('update-target-url', this.handleUpdateTarget);
+ this.webContentsView.webContents.on('update-target-url', this.handleUpdateTarget);
if (process.platform !== 'darwin') {
- this.browserView.webContents.on('before-input-event', this.handleInputEvents);
+ this.webContentsView.webContents.on('before-input-event', this.handleInputEvents);
}
- this.browserView.webContents.on('input-event', (_, inputEvent) => {
+ this.webContentsView.webContents.on('input-event', (_, inputEvent) => {
if (inputEvent.type === 'mouseDown') {
ipcMain.emit(CLOSE_SERVERS_DROPDOWN);
ipcMain.emit(CLOSE_DOWNLOADS_DROPDOWN);
}
});
- WebContentsEventManager.addWebContentsEventListeners(this.browserView.webContents);
+ WebContentsEventManager.addWebContentsEventListeners(this.webContentsView.webContents);
if (!DeveloperMode.get('disableContextMenu')) {
- this.contextMenu = new ContextMenu({}, this.browserView);
+ this.contextMenu = new ContextMenu({}, this.webContentsView);
}
-
this.maxRetries = MAX_SERVER_RETRIES;
this.altPressStatus = false;
@@ -116,10 +114,10 @@ export class MattermostBrowserView extends EventEmitter {
return this.loggedIn;
}
get currentURL() {
- return parseURL(this.browserView.webContents.getURL());
+ return parseURL(this.webContentsView.webContents.getURL());
}
get webContentsId() {
- return this.browserView.webContents.id;
+ return this.webContentsView.webContents.id;
}
onLogin = (loggedIn: boolean) => {
@@ -139,9 +137,9 @@ export class MattermostBrowserView extends EventEmitter {
};
goToOffset = (offset: number) => {
- if (this.browserView.webContents.navigationHistory.canGoToOffset(offset)) {
+ if (this.webContentsView.webContents.navigationHistory.canGoToOffset(offset)) {
try {
- this.browserView.webContents.navigationHistory.goToOffset(offset);
+ this.webContentsView.webContents.navigationHistory.goToOffset(offset);
this.updateHistoryButton();
} catch (error) {
this.log.error(error);
@@ -152,25 +150,25 @@ export class MattermostBrowserView extends EventEmitter {
getBrowserHistoryStatus = () => {
if (this.currentURL?.toString() === this.view.url.toString()) {
- this.browserView.webContents.navigationHistory.clear();
+ this.webContentsView.webContents.navigationHistory.clear();
this.atRoot = true;
} else {
this.atRoot = false;
}
return {
- canGoBack: this.browserView.webContents.navigationHistory.canGoBack(),
- canGoForward: this.browserView.webContents.navigationHistory.canGoForward(),
+ canGoBack: this.webContentsView.webContents.navigationHistory.canGoBack(),
+ canGoForward: this.webContentsView.webContents.navigationHistory.canGoForward(),
};
};
updateHistoryButton = () => {
const {canGoBack, canGoForward} = this.getBrowserHistoryStatus();
- this.browserView.webContents.send(BROWSER_HISTORY_STATUS_UPDATED, canGoBack, canGoForward);
+ this.webContentsView.webContents.send(BROWSER_HISTORY_STATUS_UPDATED, canGoBack, canGoForward);
};
load = (someURL?: URL | string) => {
- if (!this.browserView) {
+ if (!this.webContentsView) {
return;
}
@@ -188,11 +186,11 @@ export class MattermostBrowserView extends EventEmitter {
}
this.log.verbose(`Loading ${loadURL}`);
if (this.view.type === TAB_MESSAGING) {
- performanceMonitor.registerServerView(`Server ${this.browserView.webContents.id}`, this.browserView.webContents, this.view.server.id);
+ performanceMonitor.registerServerView(`Server ${this.webContentsView.webContents.id}`, this.webContentsView.webContents, this.view.server.id);
} else {
- performanceMonitor.registerView(`Server ${this.browserView.webContents.id}`, this.browserView.webContents, this.view.server.id);
+ performanceMonitor.registerView(`Server ${this.webContentsView.webContents.id}`, this.webContentsView.webContents, this.view.server.id);
}
- const loading = this.browserView.webContents.loadURL(loadURL, {userAgent: composeUserAgent(DeveloperMode.get('browserOnly'))});
+ const loading = this.webContentsView.webContents.loadURL(loadURL, {userAgent: composeUserAgent(DeveloperMode.get('browserOnly'))});
loading.then(this.loadSuccess(loadURL)).catch((err) => {
if (err.code && err.code.startsWith('ERR_CERT')) {
MainWindow.sendToRenderer(LOAD_FAILED, this.id, err.toString(), loadURL.toString());
@@ -221,8 +219,7 @@ export class MattermostBrowserView extends EventEmitter {
return;
}
this.isVisible = true;
- mainWindow.addBrowserView(this.browserView);
- mainWindow.setTopBrowserView(this.browserView);
+ mainWindow.contentView.addChildView(this.webContentsView);
this.setBounds(getWindowBoundaries(mainWindow));
if (this.status === Status.READY) {
this.focus();
@@ -232,7 +229,7 @@ export class MattermostBrowserView extends EventEmitter {
hide = () => {
if (this.isVisible) {
this.isVisible = false;
- MainWindow.get()?.removeBrowserView(this.browserView);
+ MainWindow.get()?.contentView.removeChildView(this.webContentsView);
}
};
@@ -243,23 +240,23 @@ export class MattermostBrowserView extends EventEmitter {
};
getBounds = () => {
- return this.browserView.getBounds();
+ return this.webContentsView.getBounds();
};
openFind = () => {
- this.browserView.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
+ this.webContentsView.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
};
setBounds = (boundaries: Electron.Rectangle) => {
- this.browserView.setBounds(boundaries);
+ this.webContentsView.setBounds(boundaries);
};
destroy = () => {
WebContentsEventManager.removeWebContentsListeners(this.webContentsId);
AppState.clear(this.id);
- MainWindow.get()?.removeBrowserView(this.browserView);
- performanceMonitor.unregisterView(this.browserView.webContents.id);
- this.browserView.webContents.close();
+ performanceMonitor.unregisterView(this.webContentsView.webContents.id);
+ MainWindow.get()?.contentView.removeChildView(this.webContentsView);
+ this.webContentsView.webContents.close();
this.isVisible = false;
if (this.retryLoad) {
@@ -311,17 +308,17 @@ export class MattermostBrowserView extends EventEmitter {
// So what we do here is check to see if it's opened correctly and if not we reset it
if (process.platform === 'darwin') {
const timeout = setTimeout(() => {
- if (this.browserView.webContents.isDevToolsOpened()) {
- this.browserView.webContents.closeDevTools();
- this.browserView.webContents.openDevTools({mode: 'detach'});
+ if (this.webContentsView.webContents.isDevToolsOpened()) {
+ this.webContentsView.webContents.closeDevTools();
+ this.webContentsView.webContents.openDevTools({mode: 'detach'});
}
}, 500);
- this.browserView.webContents.on('devtools-opened', () => {
+ this.webContentsView.webContents.on('devtools-opened', () => {
clearTimeout(timeout);
});
}
- this.browserView.webContents.openDevTools({mode: 'detach'});
+ this.webContentsView.webContents.openDevTools({mode: 'detach'});
};
/**
@@ -329,16 +326,16 @@ export class MattermostBrowserView extends EventEmitter {
*/
sendToRenderer = (channel: string, ...args: any[]) => {
- this.browserView.webContents.send(channel, ...args);
+ this.webContentsView.webContents.send(channel, ...args);
};
isDestroyed = () => {
- return this.browserView.webContents.isDestroyed();
+ return this.webContentsView.webContents.isDestroyed();
};
focus = () => {
- if (this.browserView.webContents) {
- this.browserView.webContents.focus();
+ if (this.webContentsView.webContents) {
+ this.webContentsView.webContents.focus();
} else {
this.log.warn('trying to focus the browserview, but it doesn\'t yet have webcontents.');
}
@@ -381,10 +378,10 @@ export class MattermostBrowserView extends EventEmitter {
private retry = (loadURL: string) => {
return () => {
// window was closed while retrying
- if (!this.browserView || !this.browserView.webContents) {
+ if (!this.webContentsView || !this.webContentsView.webContents) {
return;
}
- const loading = this.browserView.webContents.loadURL(loadURL, {userAgent: composeUserAgent(DeveloperMode.get('browserOnly'))});
+ const loading = this.webContentsView.webContents.loadURL(loadURL, {userAgent: composeUserAgent(DeveloperMode.get('browserOnly'))});
loading.then(this.loadSuccess(loadURL)).catch((err) => {
if (this.maxRetries-- > 0) {
this.loadRetry(loadURL, err);
@@ -402,7 +399,7 @@ export class MattermostBrowserView extends EventEmitter {
private retryInBackground = (loadURL: string) => {
return () => {
// window was closed while retrying
- if (!this.browserView || !this.browserView.webContents) {
+ if (!this.webContentsView || !this.webContentsView.webContents) {
return;
}
const parsedURL = parseURL(loadURL);
diff --git a/src/main/views/downloadsDropdownMenuView.test.js b/src/main/views/downloadsDropdownMenuView.test.js
index ddbe8202..9112c674 100644
--- a/src/main/views/downloadsDropdownMenuView.test.js
+++ b/src/main/views/downloadsDropdownMenuView.test.js
@@ -30,7 +30,8 @@ jest.mock('electron', () => {
getAppPath: () => '',
getPath: jest.fn(() => '/valid/downloads/path'),
},
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
+ setBackgroundColor: jest.fn(),
webContents: {
loadURL: jest.fn(),
focus: jest.fn(),
@@ -69,7 +70,7 @@ jest.mock('fs', () => ({
describe('main/views/DownloadsDropdownMenuView', () => {
beforeEach(() => {
- MainWindow.get.mockReturnValue({addBrowserView: jest.fn(), setTopBrowserView: jest.fn()});
+ MainWindow.get.mockReturnValue({contentView: {addChildView: jest.fn()}});
MainWindow.getBounds.mockReturnValue({width: 800, height: 600, x: 0, y: 0});
getDarwinDoNotDisturb.mockReturnValue(false);
});
diff --git a/src/main/views/downloadsDropdownMenuView.ts b/src/main/views/downloadsDropdownMenuView.ts
index 6036e8d6..ce48a082 100644
--- a/src/main/views/downloadsDropdownMenuView.ts
+++ b/src/main/views/downloadsDropdownMenuView.ts
@@ -1,7 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {IpcMainEvent} from 'electron';
-import {BrowserView, ipcMain} from 'electron';
+import {WebContentsView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN_MENU,
@@ -37,7 +37,7 @@ const log = new Logger('DownloadsDropdownMenuView');
export class DownloadsDropdownMenuView {
private open: boolean;
- private view?: BrowserView;
+ private view?: WebContentsView;
private bounds?: Electron.Rectangle;
private item?: DownloadedItem;
private coordinates?: CoordinatesToJsonType;
@@ -66,19 +66,11 @@ export class DownloadsDropdownMenuView {
throw new Error('Cannot initialize downloadsDropdownMenuView, missing MainWindow');
}
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT);
-
- const preload = getLocalPreload('internalAPI.js');
- this.view = new BrowserView({webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
+ this.view = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
+ this.view.setBackgroundColor('#00000000');
performanceMonitor.registerView('DownloadsDropdownMenuView', this.view.webContents);
this.view.webContents.loadURL('mattermost-desktop://renderer/downloadsDropdownMenu.html');
- MainWindow.get()?.addBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
};
/**
@@ -128,7 +120,7 @@ export class DownloadsDropdownMenuView {
this.item = item;
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT);
this.view.setBounds(this.bounds);
- MainWindow.get()?.setTopBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
this.view.webContents.focus();
this.updateDownloadsDropdownMenu();
};
diff --git a/src/main/views/downloadsDropdownView.test.js b/src/main/views/downloadsDropdownView.test.js
index 74b65ac9..0f71b4c2 100644
--- a/src/main/views/downloadsDropdownView.test.js
+++ b/src/main/views/downloadsDropdownView.test.js
@@ -38,7 +38,8 @@ jest.mock('electron', () => {
getAppPath: () => '',
getPath: jest.fn(() => '/valid/downloads/path'),
},
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
+ setBackgroundColor: jest.fn(),
webContents: {
loadURL: jest.fn(),
focus: jest.fn(),
@@ -77,7 +78,7 @@ jest.mock('main/windows/mainWindow', () => ({
describe('main/views/DownloadsDropdownView', () => {
beforeEach(() => {
- MainWindow.get.mockReturnValue({addBrowserView: jest.fn(), setTopBrowserView: jest.fn()});
+ MainWindow.get.mockReturnValue({contentView: {addChildView: jest.fn()}});
getDarwinDoNotDisturb.mockReturnValue(false);
});
describe('getBounds', () => {
diff --git a/src/main/views/downloadsDropdownView.ts b/src/main/views/downloadsDropdownView.ts
index dc3194f0..453a67d6 100644
--- a/src/main/views/downloadsDropdownView.ts
+++ b/src/main/views/downloadsDropdownView.ts
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import type {IpcMainEvent} from 'electron';
-import {BrowserView, ipcMain} from 'electron';
+import {WebContentsView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN,
@@ -33,7 +33,7 @@ export class DownloadsDropdownView {
private bounds?: Electron.Rectangle;
private windowBounds?: Electron.Rectangle;
private item?: DownloadedItem;
- private view?: BrowserView;
+ private view?: WebContentsView;
constructor() {
MainWindow.on(MAIN_WINDOW_CREATED, this.init);
@@ -55,20 +55,11 @@ export class DownloadsDropdownView {
throw new Error('Cannot initialize, no main window');
}
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT);
-
- const preload = getLocalPreload('internalAPI.js');
- this.view = new BrowserView({webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
-
+ this.view = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
+ this.view.setBackgroundColor('#00000000');
performanceMonitor.registerView('DownloadsDropdownView', this.view.webContents);
this.view.webContents.loadURL('mattermost-desktop://renderer/downloadsDropdown.html');
- MainWindow.get()?.addBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
};
/**
@@ -109,7 +100,7 @@ export class DownloadsDropdownView {
}
this.view.setBounds(this.bounds);
- MainWindow.get()?.setTopBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
this.view.webContents.focus();
downloadsManager.onOpen();
MainWindow.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN);
diff --git a/src/main/views/loadingScreen.test.js b/src/main/views/loadingScreen.test.js
index de34b959..0f888969 100644
--- a/src/main/views/loadingScreen.test.js
+++ b/src/main/views/loadingScreen.test.js
@@ -21,9 +21,10 @@ jest.mock('main/windows/mainWindow', () => ({
describe('main/views/loadingScreen', () => {
describe('show', () => {
const mainWindow = {
- getBrowserViews: jest.fn(),
- setTopBrowserView: jest.fn(),
- addBrowserView: jest.fn(),
+ contentView: {
+ addChildView: jest.fn(),
+ children: [],
+ },
};
const loadingScreen = new LoadingScreen();
loadingScreen.create = jest.fn();
@@ -31,7 +32,7 @@ describe('main/views/loadingScreen', () => {
const view = {webContents: {send: jest.fn(), isLoading: () => false}};
beforeEach(() => {
- mainWindow.getBrowserViews.mockImplementation(() => []);
+ mainWindow.contentView.children = [];
MainWindow.get.mockReturnValue(mainWindow);
});
@@ -46,14 +47,14 @@ describe('main/views/loadingScreen', () => {
});
loadingScreen.show();
expect(loadingScreen.create).toHaveBeenCalled();
- expect(mainWindow.addBrowserView).toHaveBeenCalled();
+ expect(mainWindow.contentView.addChildView).toHaveBeenCalled();
});
it('should set the browser view as top if already exists and needs to be shown', () => {
loadingScreen.view = view;
- mainWindow.getBrowserViews.mockImplementation(() => [view]);
+ mainWindow.contentView.children = [view];
loadingScreen.show();
- expect(mainWindow.setTopBrowserView).toHaveBeenCalled();
+ expect(mainWindow.contentView.addChildView).toHaveBeenCalled();
});
});
});
diff --git a/src/main/views/loadingScreen.ts b/src/main/views/loadingScreen.ts
index ef08b5a9..1c152fe2 100644
--- a/src/main/views/loadingScreen.ts
+++ b/src/main/views/loadingScreen.ts
@@ -1,7 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {BrowserView, app, ipcMain} from 'electron';
+import {WebContentsView, app, ipcMain} from 'electron';
import {DARK_MODE_CHANGE, LOADING_SCREEN_ANIMATION_FINISHED, MAIN_WINDOW_RESIZED, TOGGLE_LOADING_SCREEN_VISIBILITY} from 'common/communication';
import {Logger} from 'common/log';
@@ -18,7 +18,7 @@ enum LoadingScreenState {
const log = new Logger('LoadingScreen');
export class LoadingScreen {
- private view?: BrowserView;
+ private view?: WebContentsView;
private state: LoadingScreenState;
constructor() {
@@ -55,15 +55,11 @@ export class LoadingScreen {
if (this.view?.webContents.isLoading()) {
this.view.webContents.once('did-finish-load', () => {
this.view!.webContents.send(TOGGLE_LOADING_SCREEN_VISIBILITY, true);
+ mainWindow.contentView.addChildView(this.view!);
});
} else {
this.view!.webContents.send(TOGGLE_LOADING_SCREEN_VISIBILITY, true);
- }
-
- if (mainWindow.getBrowserViews().includes(this.view!)) {
- mainWindow.setTopBrowserView(this.view!);
- } else {
- mainWindow.addBrowserView(this.view!);
+ mainWindow.contentView.addChildView(this.view!);
}
this.setBounds();
@@ -77,19 +73,9 @@ export class LoadingScreen {
};
private create = () => {
- const preload = getLocalPreload('internalAPI.js');
- this.view = new BrowserView({webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
- const localURL = 'mattermost-desktop://renderer/loadingScreen.html';
-
+ this.view = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
performanceMonitor.registerView('LoadingScreen', this.view.webContents);
- this.view.webContents.loadURL(localURL);
+ this.view.webContents.loadURL('mattermost-desktop://renderer/loadingScreen.html');
};
private handleAnimationFinished = () => {
@@ -97,7 +83,9 @@ export class LoadingScreen {
if (this.view && this.state !== LoadingScreenState.HIDDEN) {
this.state = LoadingScreenState.HIDDEN;
- MainWindow.get()?.removeBrowserView(this.view);
+ MainWindow.get()?.contentView.removeChildView(this.view);
+ this.view.webContents.close();
+ delete this.view;
}
if (process.env.NODE_ENV === 'test') {
diff --git a/src/main/views/modalView.test.js b/src/main/views/modalView.test.js
index 8b0aa909..af455a9f 100644
--- a/src/main/views/modalView.test.js
+++ b/src/main/views/modalView.test.js
@@ -6,7 +6,8 @@
import {ModalView} from './modalView';
jest.mock('electron', () => ({
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
+ setBackgroundColor: jest.fn(),
webContents: {
loadURL: jest.fn(),
once: jest.fn(),
@@ -34,7 +35,12 @@ jest.mock('main/performanceMonitor', () => ({
describe('main/views/modalView', () => {
describe('show', () => {
- const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn()};
+ const window = {
+ contentView: {
+ addChildView: jest.fn(),
+ removeChildView: jest.fn(),
+ },
+ };
const onResolve = jest.fn();
const onReject = jest.fn();
let modalView;
@@ -56,15 +62,15 @@ describe('main/views/modalView', () => {
it('should add to window', () => {
modalView.show();
- expect(window.addBrowserView).toBeCalledWith(modalView.view);
+ expect(window.contentView.addChildView).toBeCalledWith(modalView.view);
expect(modalView.status).toBe(1);
});
it('should reattach if already attached', () => {
modalView.windowAttached = window;
modalView.show();
- expect(window.removeBrowserView).toBeCalledWith(modalView.view);
- expect(window.addBrowserView).toBeCalledWith(modalView.view);
+ expect(window.contentView.removeChildView).toBeCalledWith(modalView.view);
+ expect(window.contentView.addChildView).toBeCalledWith(modalView.view);
});
it('should delay call to focus when the modal is loading', () => {
@@ -87,7 +93,12 @@ describe('main/views/modalView', () => {
});
describe('hide', () => {
- const window = {addBrowserView: jest.fn(), removeBrowserView: jest.fn()};
+ const window = {
+ contentView: {
+ addChildView: jest.fn(),
+ removeChildView: jest.fn(),
+ },
+ };
const onResolve = jest.fn();
const onReject = jest.fn();
let modalView;
@@ -111,7 +122,7 @@ describe('main/views/modalView', () => {
it('should remove browser view and destroy web contents on hide', () => {
modalView.hide();
expect(modalView.view.webContents.close).toBeCalled();
- expect(window.removeBrowserView).toBeCalledWith(modalView.view);
+ expect(window.contentView.removeChildView).toBeCalledWith(modalView.view);
});
it('should close dev tools when open', () => {
diff --git a/src/main/views/modalView.ts b/src/main/views/modalView.ts
index 0fa591f0..f5825ca8 100644
--- a/src/main/views/modalView.ts
+++ b/src/main/views/modalView.ts
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import type {BrowserWindow} from 'electron';
-import {BrowserView} from 'electron';
+import {WebContentsView} from 'electron';
import {Logger} from 'common/log';
import performanceMonitor from 'main/performanceMonitor';
@@ -20,7 +20,7 @@ export class ModalView {
key: string;
html: string;
data: T;
- view: BrowserView;
+ view: WebContentsView;
onReject: (value: T2) => void;
onResolve: (value: T2) => void;
window: BrowserWindow;
@@ -36,14 +36,8 @@ export class ModalView {
this.data = data;
this.log = new Logger('ModalView', key);
this.log.info(`preloading with ${preload}`);
- this.view = new BrowserView({webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
+ this.view = new WebContentsView({webPreferences: {preload}});
+ this.view.setBackgroundColor('#00000000');
this.onReject = onReject;
this.onResolve = onResolve;
this.window = currentWindow;
@@ -64,11 +58,11 @@ export class ModalView {
show = (win?: BrowserWindow, withDevTools?: boolean) => {
if (this.windowAttached) {
// we'll reatach
- this.windowAttached.removeBrowserView(this.view);
+ this.windowAttached.contentView.removeChildView(this.view);
}
this.windowAttached = win || this.window;
- this.windowAttached.addBrowserView(this.view);
+ this.windowAttached.contentView.addChildView(this.view);
// Linux sometimes doesn't have the bound initialized correctly initially, so we wait to set them
const setBoundsFunction = () => {
@@ -100,8 +94,8 @@ export class ModalView {
if (this.view.webContents.isDevToolsOpened()) {
this.view.webContents.closeDevTools();
}
- this.windowAttached.removeBrowserView(this.view);
performanceMonitor.unregisterView(this.view.webContents.id);
+ this.windowAttached.contentView.removeChildView(this.view);
this.view.webContents.close();
delete this.windowAttached;
diff --git a/src/main/views/serverDropdownView.test.js b/src/main/views/serverDropdownView.test.js
index 9b4621d4..e27cc889 100644
--- a/src/main/views/serverDropdownView.test.js
+++ b/src/main/views/serverDropdownView.test.js
@@ -15,7 +15,7 @@ jest.mock('main/utils', () => ({
}));
jest.mock('electron', () => ({
- BrowserView: jest.fn().mockImplementation(() => ({
+ WebContentsView: jest.fn().mockImplementation(() => ({
webContents: {
loadURL: jest.fn(),
focus: jest.fn(),
diff --git a/src/main/views/serverDropdownView.ts b/src/main/views/serverDropdownView.ts
index 059cf7e1..1ca86a32 100644
--- a/src/main/views/serverDropdownView.ts
+++ b/src/main/views/serverDropdownView.ts
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import type {IpcMainEvent} from 'electron';
-import {BrowserView, ipcMain} from 'electron';
+import {WebContentsView, ipcMain} from 'electron';
import ServerViewState from 'app/serverViewState';
import AppState from 'common/appState';
@@ -32,7 +32,7 @@ import MainWindow from '../windows/mainWindow';
const log = new Logger('ServerDropdownView');
export class ServerDropdownView {
- private view?: BrowserView;
+ private view?: WebContentsView;
private servers: UniqueServer[];
private hasGPOServers: boolean;
private isOpen: boolean;
@@ -75,22 +75,15 @@ export class ServerDropdownView {
private init = () => {
log.info('init');
- const preload = getLocalPreload('internalAPI.js');
- this.view = new BrowserView({webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
+ this.view = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
+ this.view.setBackgroundColor('#00000000');
performanceMonitor.registerView('ServerDropdownView', this.view.webContents);
this.view.webContents.loadURL('mattermost-desktop://renderer/dropdown.html');
this.setOrderedServers();
this.windowBounds = MainWindow.getBounds();
this.updateDropdown();
- MainWindow.get()?.addBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
};
private updateDropdown = () => {
@@ -138,7 +131,7 @@ export class ServerDropdownView {
return;
}
this.view.setBounds(this.bounds);
- MainWindow.get()?.setTopBrowserView(this.view);
+ MainWindow.get()?.contentView.addChildView(this.view);
this.view.webContents.focus();
MainWindow.sendToRenderer(OPEN_SERVERS_DROPDOWN);
this.isOpen = true;
diff --git a/src/main/views/viewManager.test.js b/src/main/views/viewManager.test.js
index 025669aa..00750419 100644
--- a/src/main/views/viewManager.test.js
+++ b/src/main/views/viewManager.test.js
@@ -12,7 +12,7 @@ import PermissionsManager from 'main/permissionsManager';
import MainWindow from 'main/windows/mainWindow';
import LoadingScreen from './loadingScreen';
-import {MattermostBrowserView} from './MattermostBrowserView';
+import {MattermostWebContentsView} from './MattermostWebContentsView';
import {ViewManager} from './viewManager';
jest.mock('electron', () => ({
@@ -112,8 +112,8 @@ jest.mock('common/servers/serverManager', () => ({
}),
}));
-jest.mock('./MattermostBrowserView', () => ({
- MattermostBrowserView: jest.fn(),
+jest.mock('./MattermostWebContentsView', () => ({
+ MattermostWebContentsView: jest.fn(),
}));
jest.mock('./modalManager', () => ({
@@ -133,7 +133,7 @@ describe('main/views/viewManager', () => {
beforeEach(() => {
viewManager.showById = jest.fn();
MainWindow.get.mockReturnValue({});
- MattermostBrowserView.mockImplementation((view) => ({
+ MattermostWebContentsView.mockImplementation((view) => ({
on: jest.fn(),
load: loadFn,
once: onceFn,
@@ -181,7 +181,7 @@ describe('main/views/viewManager', () => {
beforeEach(() => {
viewManager.showById = jest.fn();
MainWindow.get.mockReturnValue({});
- MattermostBrowserView.mockImplementation((view) => ({
+ MattermostWebContentsView.mockImplementation((view) => ({
on: jest.fn(),
load: jest.fn(),
once: jest.fn(),
@@ -235,7 +235,7 @@ describe('main/views/viewManager', () => {
const onceFn = jest.fn();
const loadFn = jest.fn();
const destroyFn = jest.fn();
- MattermostBrowserView.mockImplementation((view) => ({
+ MattermostWebContentsView.mockImplementation((view) => ({
on: jest.fn(),
load: loadFn,
once: onceFn,
@@ -255,7 +255,7 @@ describe('main/views/viewManager', () => {
it('should recycle existing views', () => {
const makeSpy = jest.spyOn(viewManager, 'makeView');
- const view = new MattermostBrowserView({
+ const view = new MattermostWebContentsView({
id: 'view1',
server: {
id: 'server1',
diff --git a/src/main/views/viewManager.ts b/src/main/views/viewManager.ts
index 14357687..1a9e1790 100644
--- a/src/main/views/viewManager.ts
+++ b/src/main/views/viewManager.ts
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
-import {BrowserView, dialog, ipcMain} from 'electron';
+import {WebContentsView, dialog, ipcMain} from 'electron';
import isDev from 'electron-is-dev';
import ServerViewState from 'app/serverViewState';
@@ -51,7 +51,7 @@ import MainWindow from 'main/windows/mainWindow';
import type {DeveloperSettings} from 'types/settings';
import LoadingScreen from './loadingScreen';
-import {MattermostBrowserView} from './MattermostBrowserView';
+import {MattermostWebContentsView} from './MattermostWebContentsView';
import modalManager from './modalManager';
import {getLocalPreload, getAdjustedWindowBoundaries} from '../utils';
@@ -62,7 +62,7 @@ const URL_VIEW_HEIGHT = 20;
export class ViewManager {
private closedViews: Map;
- private views: Map;
+ private views: Map;
private currentView?: string;
private urlViewCancel?: () => void;
@@ -206,23 +206,23 @@ export class ViewManager {
if (this.closedViews.has(view.id)) {
this.openClosedView(view.id, urlWithSchema);
} else {
- const browserView = this.views.get(view.id);
- if (!browserView) {
+ const webContentsView = this.views.get(view.id);
+ if (!webContentsView) {
log.error(`Couldn't find a view matching the id ${view.id}`);
return;
}
- if (browserView.isReady() && ServerManager.getRemoteInfo(browserView.view.server.id)?.serverVersion && Utils.isVersionGreaterThanOrEqualTo(ServerManager.getRemoteInfo(browserView.view.server.id)?.serverVersion ?? '', '6.0.0')) {
- const formattedServerURL = `${browserView.view.server.url.origin}${getFormattedPathName(browserView.view.server.url.pathname)}`;
+ if (webContentsView.isReady() && ServerManager.getRemoteInfo(webContentsView.view.server.id)?.serverVersion && Utils.isVersionGreaterThanOrEqualTo(ServerManager.getRemoteInfo(webContentsView.view.server.id)?.serverVersion ?? '', '6.0.0')) {
+ const formattedServerURL = `${webContentsView.view.server.url.origin}${getFormattedPathName(webContentsView.view.server.url.pathname)}`;
const pathName = `/${urlWithSchema.replace(formattedServerURL, '')}`;
- browserView.sendToRenderer(BROWSER_HISTORY_PUSH, pathName);
- this.deeplinkSuccess(browserView.id);
+ webContentsView.sendToRenderer(BROWSER_HISTORY_PUSH, pathName);
+ this.deeplinkSuccess(webContentsView.id);
} else {
// attempting to change parsedURL protocol results in it not being modified.
- browserView.resetLoadingStatus();
- browserView.load(urlWithSchema);
- browserView.once(LOAD_SUCCESS, this.deeplinkSuccess);
- browserView.once(LOAD_FAILED, this.deeplinkFailed);
+ webContentsView.resetLoadingStatus();
+ webContentsView.load(urlWithSchema);
+ webContentsView.once(LOAD_SUCCESS, this.deeplinkSuccess);
+ webContentsView.once(LOAD_FAILED, this.deeplinkFailed);
}
}
} else {
@@ -260,26 +260,26 @@ export class ViewManager {
this.closedViews.set(view.id, {srv, view});
return;
}
- const browserView = this.makeView(srv, view, url);
- this.addView(browserView);
+ const webContentsView = this.makeView(srv, view, url);
+ this.addView(webContentsView);
};
- private makeView = (srv: MattermostServer, view: MattermostView, url?: string): MattermostBrowserView => {
+ private makeView = (srv: MattermostServer, view: MattermostView, url?: string): MattermostWebContentsView => {
const mainWindow = MainWindow.get();
if (!mainWindow) {
throw new Error('Cannot create view, no main window present');
}
- const browserView = new MattermostBrowserView(view, {webPreferences: {spellcheck: Config.useSpellChecker}});
- browserView.once(LOAD_SUCCESS, this.activateView);
- browserView.on(LOADSCREEN_END, this.finishLoading);
- browserView.on(LOAD_FAILED, this.failLoading);
- browserView.on(UPDATE_TARGET_URL, this.showURLView);
- browserView.load(url);
- return browserView;
+ const webContentsView = new MattermostWebContentsView(view, {webPreferences: {spellcheck: Config.useSpellChecker}});
+ webContentsView.once(LOAD_SUCCESS, this.activateView);
+ webContentsView.on(LOADSCREEN_END, this.finishLoading);
+ webContentsView.on(LOAD_FAILED, this.failLoading);
+ webContentsView.on(UPDATE_TARGET_URL, this.showURLView);
+ webContentsView.load(url);
+ return webContentsView;
};
- private addView = (view: MattermostBrowserView): void => {
+ private addView = (view: MattermostWebContentsView): void => {
this.views.set(view.id, view);
// Force a permission check for notifications
@@ -355,26 +355,18 @@ export class ViewManager {
}
if (url && url !== '') {
const urlString = typeof url === 'string' ? url : url.toString();
- const preload = getLocalPreload('internalAPI.js');
- const urlView = new BrowserView({
- webPreferences: {
- preload,
-
- // Workaround for this issue: https://github.com/electron/electron/issues/30993
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- transparent: true,
- }});
+ const urlView = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
+ urlView.setBackgroundColor('#00000000');
const localURL = `mattermost-desktop://renderer/urlView.html?url=${encodeURIComponent(urlString)}`;
performanceMonitor.registerView('URLView', urlView.webContents);
urlView.webContents.loadURL(localURL);
- MainWindow.get()?.addBrowserView(urlView);
+ MainWindow.get()?.contentView.addChildView(urlView);
const boundaries = this.views.get(this.currentView || '')?.getBounds() ?? MainWindow.getBounds();
const hideView = () => {
delete this.urlViewCancel;
try {
- mainWindow.removeBrowserView(urlView);
+ mainWindow.contentView.removeChildView(urlView);
} catch (e) {
log.error('Failed to remove URL view', e);
}
@@ -427,12 +419,12 @@ export class ViewManager {
const currentViewId: string | undefined = this.views.get(this.currentView as string)?.view.id;
- const current: Map = new Map();
+ const current: Map = new Map();
for (const view of this.views.values()) {
current.set(view.view.id, view);
}
- const views: Map = new Map();
+ const views: Map = new Map();
const closed: Map = new Map();
const sortedViews = ServerManager.getAllServers().flatMap((x) => ServerManager.getOrderedTabsForServer(x.id).
@@ -622,10 +614,10 @@ export class ViewManager {
this.closedViews.delete(view.id);
}
this.showById(id);
- const browserView = this.views.get(id)!;
- browserView.isVisible = true;
- browserView.on(LOAD_SUCCESS, () => {
- browserView.isVisible = false;
+ const webContentsView = this.views.get(id)!;
+ webContentsView.isVisible = true;
+ webContentsView.on(LOAD_SUCCESS, () => {
+ webContentsView.isVisible = false;
this.showById(id);
});
ipcMain.emit(OPEN_VIEW, null, view.id);
diff --git a/src/main/windows/callsWidgetWindow.ts b/src/main/windows/callsWidgetWindow.ts
index 7afaea53..471fceb0 100644
--- a/src/main/windows/callsWidgetWindow.ts
+++ b/src/main/windows/callsWidgetWindow.ts
@@ -35,7 +35,7 @@ import {
openScreensharePermissionsSettingsMacOS,
resetScreensharePermissionsMacOS,
} from 'main/utils';
-import type {MattermostBrowserView} from 'main/views/MattermostBrowserView';
+import type {MattermostWebContentsView} from 'main/views/MattermostWebContentsView';
import ViewManager from 'main/views/viewManager';
import webContentsEventManager from 'main/views/webContentEvents';
import MainWindow from 'main/windows/mainWindow';
@@ -51,7 +51,7 @@ const log = new Logger('CallsWidgetWindow');
export class CallsWidgetWindow {
private win?: BrowserWindow;
- private mainView?: MattermostBrowserView;
+ private mainView?: MattermostWebContentsView;
private options?: CallsWidgetWindowConfig;
private missingScreensharePermissions?: boolean;
@@ -135,7 +135,7 @@ export class CallsWidgetWindow {
return u.toString();
};
- private init = (view: MattermostBrowserView, options: CallsWidgetWindowConfig) => {
+ private init = (view: MattermostWebContentsView, options: CallsWidgetWindowConfig) => {
this.win = new BrowserWindow({
width: MINIMUM_CALLS_WIDGET_WIDTH,
height: MINIMUM_CALLS_WIDGET_HEIGHT,
diff --git a/src/main/windows/mainWindow.test.js b/src/main/windows/mainWindow.test.js
index 6c6673d0..7bc8c9e4 100644
--- a/src/main/windows/mainWindow.test.js
+++ b/src/main/windows/mainWindow.test.js
@@ -96,6 +96,9 @@ describe('main/windows/mainWindow', () => {
send: jest.fn(),
setWindowOpenHandler: jest.fn(),
},
+ contentView: {
+ on: jest.fn(),
+ },
isMaximized: jest.fn(),
isFullScreen: jest.fn(),
getBounds: jest.fn(),
diff --git a/src/main/windows/mainWindow.ts b/src/main/windows/mainWindow.ts
index f2f22c73..7c863470 100644
--- a/src/main/windows/mainWindow.ts
+++ b/src/main/windows/mainWindow.ts
@@ -21,7 +21,6 @@ import {
MAIN_WINDOW_CREATED,
MAIN_WINDOW_RESIZED,
MAIN_WINDOW_FOCUSED,
- VIEW_FINISHED_RESIZING,
TOGGLE_SECURE_INPUT,
EMIT_CONFIGURATION,
EXIT_FULLSCREEN,
@@ -49,18 +48,14 @@ export class MainWindow extends EventEmitter {
private savedWindowState?: Partial;
private ready: boolean;
- private isResizing: boolean;
- private lastEmittedBounds?: Electron.Rectangle;
constructor() {
super();
// Create the browser window.
this.ready = false;
- this.isResizing = false;
ipcMain.handle(GET_FULL_SCREEN_STATUS, () => this.win?.isFullScreen());
- ipcMain.on(VIEW_FINISHED_RESIZING, this.handleViewFinishedResizing);
ipcMain.on(EMIT_CONFIGURATION, this.handleUpdateTitleBarOverlay);
ipcMain.on(EXIT_FULLSCREEN, this.handleExitFullScreen);
@@ -85,7 +80,7 @@ export class MainWindow extends EventEmitter {
titleBarStyle: 'hidden' as const,
titleBarOverlay: this.getTitleBarOverlay(),
trafficLightPosition: {x: 12, y: 12},
- backgroundColor: '#fff', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
+ backgroundColor: '#000', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
webPreferences: {
disableBlinkFeatures: 'Auxclick',
preload: getLocalPreload('internalAPI.js'),
@@ -131,16 +126,7 @@ export class MainWindow extends EventEmitter {
this.win.on('unresponsive', this.onUnresponsive);
this.win.on('enter-full-screen', this.onEnterFullScreen);
this.win.on('leave-full-screen', this.onLeaveFullScreen);
- this.win.on('will-resize', this.onWillResize);
- this.win.on('resized', this.onResized);
- if (process.platform === 'win32') {
- // We don't want this on macOS, it's an alias of 'move'
- // This is mostly a fix for Windows 11 snapping
- this.win.on('moved', this.onResized);
- }
- if (process.platform !== 'darwin') {
- this.win.on('resize', this.onResize);
- }
+ this.win.contentView.on('bounds-changed', this.handleBoundsChanged);
this.win.webContents.on('before-input-event', this.onBeforeInputEvent);
// Should not allow the main window to generate a window of its own
@@ -457,82 +443,16 @@ export class MainWindow extends EventEmitter {
});
};
- private emitBounds = (bounds?: Electron.Rectangle, force?: boolean) => {
- // Workaround since the window bounds aren't updated immediately when the window is maximized for some reason
- // We also don't want to force too many resizes so we throttle here
- setTimeout(() => {
- const newBounds = bounds ?? this.getBounds();
- if (!force && newBounds?.height === this.lastEmittedBounds?.height && newBounds?.width === this.lastEmittedBounds?.width) {
- return;
- }
-
- // For some reason on Linux I've seen the menu bar popup again
- this.win?.setMenuBarVisibility(false);
-
- this.emit(MAIN_WINDOW_RESIZED, newBounds);
- this.lastEmittedBounds = newBounds;
- }, 10);
- };
-
private onEnterFullScreen = () => {
this.win?.webContents.send('enter-full-screen');
- this.emitBounds();
};
private onLeaveFullScreen = () => {
this.win?.webContents.send('leave-full-screen');
- this.emitBounds();
};
- /**
- * Resizing code
- */
-
- private onWillResize = (event: Event, newBounds: Electron.Rectangle) => {
- log.silly('onWillResize', newBounds);
-
- /**
- * Fixes an issue on win11 related to Snap where the first "will-resize" event would return the same bounds
- * causing the "resize" event to not fire
- */
- const prevBounds = this.getBounds();
- if (prevBounds?.height === newBounds.height && prevBounds?.width === newBounds.width) {
- log.debug('prevented resize');
- event.preventDefault();
- return;
- }
-
- // Workaround for macOS to stop the window from sending too many resize calls to the BrowserViews
- if (process.platform === 'darwin' && this.isResizing) {
- log.debug('prevented resize');
- event.preventDefault();
- return;
- }
-
- this.isResizing = true;
- this.emitBounds(newBounds);
- };
-
- private onResize = () => {
- log.silly('onResize');
-
- // Workaround for Windows to stop the window from sending too many resize calls to the BrowserViews
- if (process.platform === 'win32' && this.isResizing) {
- return;
- }
- this.emitBounds();
- };
-
- private onResized = () => {
- log.debug('onResized');
-
- // Because this is the final window state after a resize, we force the size here
- this.emitBounds(this.getBounds(), true);
- this.isResizing = false;
- };
-
- private handleViewFinishedResizing = () => {
- this.isResizing = false;
+ private handleBoundsChanged = () => {
+ this.emit(MAIN_WINDOW_RESIZED, this.win?.contentView.getBounds());
};
private handleExitFullScreen = () => {
diff --git a/src/renderer/components/ErrorView.tsx b/src/renderer/components/ErrorView.tsx
index 716cc9cc..e372d039 100644
--- a/src/renderer/components/ErrorView.tsx
+++ b/src/renderer/components/ErrorView.tsx
@@ -94,7 +94,7 @@ export default function ErrorView(props: Props) {
{
setShowContent(true);
-
- // Let the main process know when the window has finished resizing
- // This is to reduce the amount of white box that happens when expand the BrowserView
- window.addEventListener('resize', () => {
- window.desktop.viewFinishedResizing();
- });
}, []);
const slides = useMemo(() => [
diff --git a/src/types/window.ts b/src/types/window.ts
index 8b7f2e67..a0616e03 100644
--- a/src/types/window.ts
+++ b/src/types/window.ts
@@ -95,7 +95,6 @@ declare global {
openWindowsCameraPreferences: () => void;
openWindowsMicrophonePreferences: () => void;
getMediaAccessStatus: (mediaType: 'microphone' | 'camera' | 'screen') => Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>;
- viewFinishedResizing: () => void;
modals: {
cancelModal: (data?: T) => void;