Remove use of getView()
and replace with getViewByURL()
where necessary. (#2544)
* WIP * Replace getView with getViewByURL
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import * as TabView from 'common/tabs/TabView';
|
||||
|
||||
describe('common/tabs/TabView', () => {
|
||||
describe('getServerView', () => {
|
||||
it('should return correct URL on messaging tab', () => {
|
||||
const server = new MattermostServer('server-1', 'http://server-1.com');
|
||||
const tab = {name: TabView.TAB_MESSAGING};
|
||||
expect(TabView.getServerView(server, tab).url).toBe(server.url);
|
||||
});
|
||||
|
||||
it('should return correct URL on playbooks tab', () => {
|
||||
const server = new MattermostServer('server-1', 'http://server-1.com');
|
||||
const tab = {name: TabView.TAB_PLAYBOOKS};
|
||||
expect(TabView.getServerView(server, tab).url.toString()).toBe(`${server.url}playbooks`);
|
||||
});
|
||||
|
||||
it('should return correct URL on boards tab', () => {
|
||||
const server = new MattermostServer('server-1', 'http://server-1.com');
|
||||
const tab = {name: TabView.TAB_FOCALBOARD};
|
||||
expect(TabView.getServerView(server, tab).url.toString()).toBe(`${server.url}boards`);
|
||||
});
|
||||
|
||||
it('should throw error on bad tab name', () => {
|
||||
const server = new MattermostServer('server-1', 'http://server-1.com');
|
||||
const tab = {name: 'not a real tab name'};
|
||||
expect(() => {
|
||||
TabView.getServerView(server, tab);
|
||||
}).toThrow(Error);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,14 +1,10 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Tab, Team} from 'types/config';
|
||||
import {Team} from 'types/config';
|
||||
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
|
||||
import MessagingTabView from './MessagingTabView';
|
||||
import FocalboardTabView from './FocalboardTabView';
|
||||
import PlaybooksTabView from './PlaybooksTabView';
|
||||
|
||||
export const TAB_MESSAGING = 'TAB_MESSAGING';
|
||||
export const TAB_FOCALBOARD = 'TAB_FOCALBOARD';
|
||||
export const TAB_PLAYBOOKS = 'TAB_PLAYBOOKS';
|
||||
@@ -46,19 +42,6 @@ export function getDefaultTeamWithTabsFromTeam(team: Team) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getServerView(srv: MattermostServer, tab: Tab) {
|
||||
switch (tab.name) {
|
||||
case TAB_MESSAGING:
|
||||
return new MessagingTabView(srv);
|
||||
case TAB_FOCALBOARD:
|
||||
return new FocalboardTabView(srv);
|
||||
case TAB_PLAYBOOKS:
|
||||
return new PlaybooksTabView(srv);
|
||||
default:
|
||||
throw new Error('Not implemeneted');
|
||||
}
|
||||
}
|
||||
|
||||
export function getTabDisplayName(tabType: TabType) {
|
||||
switch (tabType) {
|
||||
case TAB_MESSAGING:
|
||||
|
@@ -172,86 +172,6 @@ describe('common/utils/url', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getView', () => {
|
||||
const servers = [
|
||||
{
|
||||
name: 'server-1',
|
||||
url: 'http://server-1.com',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
{
|
||||
name: 'tab-type1',
|
||||
},
|
||||
{
|
||||
name: 'tab-type2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'server-2',
|
||||
url: 'http://server-2.com/subpath',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab-type1',
|
||||
},
|
||||
{
|
||||
name: 'tab-type2',
|
||||
},
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
it('should match the correct server - base URL', () => {
|
||||
const inputURL = new URL('http://server-1.com');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-1_tab', url: 'http://server-1.com/'});
|
||||
});
|
||||
|
||||
it('should match the correct server - base tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/team');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-1_tab', url: 'http://server-1.com/'});
|
||||
});
|
||||
|
||||
it('should match the correct server - different tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/type1/app');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-1_tab-type1', url: 'http://server-1.com/type1'});
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL without', () => {
|
||||
const inputURL = new URL('http://server-2.com');
|
||||
expect(urlUtils.getView(inputURL, servers)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL with wrong subpath', () => {
|
||||
const inputURL = new URL('http://server-2.com/different/subpath');
|
||||
expect(urlUtils.getView(inputURL, servers)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base URL', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-2_tab', url: 'http://server-2.com/subpath/'});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/team');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-2_tab', url: 'http://server-2.com/subpath/'});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - different tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/type2/team');
|
||||
expect(urlUtils.getView(inputURL, servers)).toStrictEqual({name: 'server-2_tab-type2', url: 'http://server-2.com/subpath/type2'});
|
||||
});
|
||||
|
||||
it('should return undefined for wrong server', () => {
|
||||
const inputURL = new URL('http://server-3.com');
|
||||
expect(urlUtils.getView(inputURL, servers)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('equalUrls', () => {
|
||||
it('base urls', () => {
|
||||
const url1 = new URL('http://server-1.com');
|
||||
@@ -321,110 +241,38 @@ describe('common/utils/url', () => {
|
||||
it('should match correct URL', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/oauth/authorize',
|
||||
{
|
||||
url: 'http://server.com',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(true);
|
||||
'http://server.com',
|
||||
)).toBe(true);
|
||||
});
|
||||
it('should not match incorrect URL', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/oauth/notauthorize',
|
||||
{
|
||||
url: 'http://server.com',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(false);
|
||||
'http://server.com',
|
||||
)).toBe(false);
|
||||
});
|
||||
it('should not match base URL', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/',
|
||||
{
|
||||
url: 'http://server.com',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(false);
|
||||
'http://server.com',
|
||||
)).toBe(false);
|
||||
});
|
||||
it('should match with subpath', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/subpath/oauth/authorize',
|
||||
{
|
||||
url: 'http://server.com/subpath',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com/subpath',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(true);
|
||||
'http://server.com/subpath',
|
||||
)).toBe(true);
|
||||
});
|
||||
it('should not match with different subpath', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/subpath/oauth/authorize',
|
||||
{
|
||||
url: 'http://server.com/different/subpath',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com/different/subpath',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(false);
|
||||
'http://server.com/different/subpath',
|
||||
)).toBe(false);
|
||||
});
|
||||
it('should not match with oauth subpath', () => {
|
||||
expect(urlUtils.isCustomLoginURL(
|
||||
'http://server.com/oauth/authorize',
|
||||
{
|
||||
url: 'http://server.com/oauth/authorize',
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
url: 'http://server.com/oauth/authorize',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
])).toBe(false);
|
||||
'http://server.com/oauth/authorize',
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -3,12 +3,7 @@
|
||||
|
||||
import {isHttpsUri, isHttpUri, isUri} from 'valid-url';
|
||||
|
||||
import {TeamWithTabs} from 'types/config';
|
||||
import {ServerFromURL} from 'types/utils';
|
||||
|
||||
import buildConfig from 'common/config/buildConfig';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import {getServerView} from 'common/tabs/TabView';
|
||||
import {customLoginRegexPaths, nonTeamUrlPaths} from 'common/utils/constants';
|
||||
|
||||
function isValidURL(testURL: string) {
|
||||
@@ -126,43 +121,6 @@ function isManagedResource(serverUrl: URL | string, inputURL: URL | string) {
|
||||
return paths.some((testPath) => isUrlType(testPath, serverUrl, inputURL));
|
||||
}
|
||||
|
||||
function getView(inputURL: URL | string, teams: TeamWithTabs[], ignoreScheme = false): ServerFromURL | undefined {
|
||||
const parsedURL = parseURL(inputURL);
|
||||
if (!parsedURL) {
|
||||
return undefined;
|
||||
}
|
||||
let firstOption;
|
||||
let secondOption;
|
||||
teams.forEach((team) => {
|
||||
const srv = new MattermostServer(team.name, team.url);
|
||||
|
||||
// sort by length so that we match the highest specificity last
|
||||
const filteredTabs = team.tabs.map((tab) => {
|
||||
const tabView = getServerView(srv, tab);
|
||||
const parsedServerUrl = parseURL(tabView.url);
|
||||
return {tabView, parsedServerUrl};
|
||||
});
|
||||
|
||||
filteredTabs.sort((a, b) => a.tabView.url.toString().length - b.tabView.url.toString().length);
|
||||
filteredTabs.forEach((tab) => {
|
||||
if (tab.parsedServerUrl) {
|
||||
// check server and subpath matches (without subpath pathname is \ so it always matches)
|
||||
if (getFormattedPathName(tab.parsedServerUrl.pathname) !== '/' && equalUrlsWithSubpath(tab.parsedServerUrl, parsedURL, ignoreScheme)) {
|
||||
firstOption = {name: tab.tabView.name, url: tab.parsedServerUrl.toString()};
|
||||
}
|
||||
if (getFormattedPathName(tab.parsedServerUrl.pathname) === '/' && equalUrlsIgnoringSubpath(tab.parsedServerUrl, parsedURL, ignoreScheme)) {
|
||||
// in case the user added something on the path that doesn't really belong to the server
|
||||
// there might be more than one that matches, but we can't differentiate, so last one
|
||||
// is as good as any other in case there is no better match (e.g.: two subpath servers with the same origin)
|
||||
// e.g.: https://community.mattermost.com/core
|
||||
secondOption = {name: tab.tabView.name, url: tab.parsedServerUrl.toString()};
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return firstOption || secondOption;
|
||||
}
|
||||
|
||||
// next two functions are defined to clarify intent
|
||||
export function equalUrlsWithSubpath(url1: URL, url2: URL, ignoreScheme?: boolean) {
|
||||
if (ignoreScheme) {
|
||||
@@ -178,24 +136,26 @@ export function equalUrlsIgnoringSubpath(url1: URL, url2: URL, ignoreScheme?: bo
|
||||
return url1.origin.toLowerCase() === url2.origin.toLowerCase();
|
||||
}
|
||||
|
||||
function isTrustedURL(url: URL | string, teams: TeamWithTabs[]) {
|
||||
function isTrustedURL(url: URL | string, rootURL: URL | string) {
|
||||
const parsedURL = parseURL(url);
|
||||
if (!parsedURL) {
|
||||
const rootParsedURL = parseURL(rootURL);
|
||||
if (!parsedURL || !rootParsedURL) {
|
||||
return false;
|
||||
}
|
||||
return getView(parsedURL, teams) !== null;
|
||||
return (getFormattedPathName(rootParsedURL.pathname) !== '/' && equalUrlsWithSubpath(rootParsedURL, parsedURL)) ||
|
||||
(getFormattedPathName(rootParsedURL.pathname) === '/' && equalUrlsIgnoringSubpath(rootParsedURL, parsedURL));
|
||||
}
|
||||
|
||||
function isCustomLoginURL(url: URL | string, server: ServerFromURL, teams: TeamWithTabs[]): boolean {
|
||||
const serverURL = parseURL(server.url);
|
||||
const subpath = server && serverURL ? serverURL.pathname : '';
|
||||
function isCustomLoginURL(url: URL | string, serverURL: URL | string): boolean {
|
||||
const parsedServerURL = parseURL(serverURL);
|
||||
const parsedURL = parseURL(url);
|
||||
if (!parsedURL) {
|
||||
if (!parsedURL || !parsedServerURL) {
|
||||
return false;
|
||||
}
|
||||
if (!isTrustedURL(parsedURL, teams)) {
|
||||
if (!isTrustedURL(parsedURL, parsedServerURL)) {
|
||||
return false;
|
||||
}
|
||||
const subpath = parsedServerURL.pathname;
|
||||
const urlPath = parsedURL.pathname;
|
||||
const replacement = subpath.endsWith('/') ? '/' : '';
|
||||
const replacedPath = urlPath.replace(subpath, replacement);
|
||||
@@ -229,7 +189,6 @@ export default {
|
||||
isValidURI,
|
||||
isInternalURL,
|
||||
parseURL,
|
||||
getView,
|
||||
getServerInfo,
|
||||
isAdminUrl,
|
||||
isTeamUrl,
|
||||
|
@@ -153,6 +153,7 @@ jest.mock('main/windows/windowManager', () => ({
|
||||
sendToMattermostViews: jest.fn(),
|
||||
sendToRenderer: jest.fn(),
|
||||
getServerNameByWebContentsId: jest.fn(),
|
||||
getServerURLFromWebContentsId: jest.fn(),
|
||||
}));
|
||||
describe('main/app/initialize', () => {
|
||||
beforeEach(() => {
|
||||
@@ -237,6 +238,7 @@ describe('main/app/initialize', () => {
|
||||
});
|
||||
|
||||
it('should allow permission requests for supported types from trusted URLs', async () => {
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://server-1.com'));
|
||||
let callback = jest.fn();
|
||||
session.defaultSession.setPermissionRequestHandler.mockImplementation((cb) => {
|
||||
cb({id: 1, getURL: () => 'http://server-1.com'}, 'bad-permission', callback);
|
||||
|
@@ -399,9 +399,15 @@ function initializeAfterAppReady() {
|
||||
}
|
||||
|
||||
const requestingURL = webContents.getURL();
|
||||
const serverURL = WindowManager.getServerURLFromWebContentsId(webContents.id);
|
||||
|
||||
if (!serverURL) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// is the requesting url trusted?
|
||||
callback(urlUtils.isTrustedURL(requestingURL, Config.teams));
|
||||
callback(urlUtils.isTrustedURL(requestingURL, serverURL));
|
||||
});
|
||||
|
||||
// only check for non-Windows, as with Windows we have to wait for GPO teams
|
||||
|
@@ -58,12 +58,6 @@ jest.mock('common/utils/url', () => {
|
||||
const actualUrl = jest.requireActual('common/utils/url');
|
||||
return {
|
||||
...actualUrl.default,
|
||||
getView: (url) => {
|
||||
if (url.toString() === 'http://badurl.com/') {
|
||||
return null;
|
||||
}
|
||||
return {name: 'test', url};
|
||||
},
|
||||
isTrustedURL: (url) => {
|
||||
return url.toString() === 'http://trustedurl.com/';
|
||||
},
|
||||
@@ -92,6 +86,7 @@ jest.mock('main/trustedOrigins', () => ({
|
||||
|
||||
jest.mock('main/windows/windowManager', () => ({
|
||||
getMainWindow: jest.fn().mockImplementation(() => ({})),
|
||||
getServerURLFromWebContentsId: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('main/views/modalManager', () => ({
|
||||
@@ -109,45 +104,45 @@ describe('main/authManager', () => {
|
||||
authManager.popLoginModal = jest.fn();
|
||||
authManager.popPermissionModal = jest.fn();
|
||||
|
||||
it('should not pop any modal on null url', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: null}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).not.toBeCalled();
|
||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should not pop any modal on null server', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://badurl.com/'}, null, jest.fn());
|
||||
it('should not pop any modal on a missing server', () => {
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(undefined);
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 0}, {url: 'http://badurl.com/'}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).not.toBeCalled();
|
||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should popLoginModal when isTrustedURL', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://trustedurl.com/'}, null, jest.fn());
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://trustedurl.com/'));
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://trustedurl.com/'}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).toBeCalled();
|
||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should popLoginModal when isCustomLoginURL', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://customloginurl.com/'}, null, jest.fn());
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://customloginurl.com/'));
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://customloginurl.com/'}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).toBeCalled();
|
||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should popLoginModal when has permission', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://haspermissionurl.com/'}, null, jest.fn());
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://haspermissionurl.com/'));
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://haspermissionurl.com/'}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).toBeCalled();
|
||||
expect(authManager.popPermissionModal).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should popPermissionModal when anything else is true', () => {
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://someotherurl.com/'}, null, jest.fn());
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://someotherurl.com/'));
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://someotherurl.com/'}, null, jest.fn());
|
||||
expect(authManager.popLoginModal).not.toBeCalled();
|
||||
expect(authManager.popPermissionModal).toBeCalled();
|
||||
});
|
||||
|
||||
it('should set login callback when logging in', () => {
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(new URL('http://someotherurl.com/'));
|
||||
const callback = jest.fn();
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, null, {url: 'http://someotherurl.com/'}, null, callback);
|
||||
authManager.handleAppLogin({preventDefault: jest.fn()}, {id: 1}, {url: 'http://someotherurl.com/'}, null, callback);
|
||||
expect(authManager.loginCallbackMap.get('http://someotherurl.com/')).toEqual(callback);
|
||||
});
|
||||
});
|
||||
|
@@ -6,7 +6,6 @@ import log from 'electron-log';
|
||||
import {PermissionType} from 'types/trustedOrigin';
|
||||
import {LoginModalData} from 'types/auth';
|
||||
|
||||
import Config from 'common/config';
|
||||
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
|
||||
import urlUtils from 'common/utils/url';
|
||||
|
||||
@@ -39,13 +38,13 @@ export class AuthManager {
|
||||
if (!parsedURL) {
|
||||
return;
|
||||
}
|
||||
const server = urlUtils.getView(parsedURL, Config.teams);
|
||||
if (!server) {
|
||||
const serverURL = WindowManager.getServerURLFromWebContentsId(webContents.id);
|
||||
if (!serverURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loginCallbackMap.set(request.url, callback); // if callback is undefined set it to null instead so we know we have set it up with no value
|
||||
if (urlUtils.isTrustedURL(request.url, Config.teams) || urlUtils.isCustomLoginURL(parsedURL, server, Config.teams) || TrustedOriginsStore.checkPermission(request.url, BASIC_AUTH_PERMISSION)) {
|
||||
if (urlUtils.isTrustedURL(request.url, serverURL) || urlUtils.isCustomLoginURL(parsedURL, serverURL) || TrustedOriginsStore.checkPermission(request.url, BASIC_AUTH_PERMISSION)) {
|
||||
this.popLoginModal(request, authInfo);
|
||||
} else {
|
||||
this.popPermissionModal(request, authInfo, BASIC_AUTH_PERMISSION);
|
||||
|
@@ -9,8 +9,8 @@ import {Tuple as tuple} from '@bloomberg/record-tuple-polyfill';
|
||||
|
||||
import {BROWSER_HISTORY_PUSH, LOAD_SUCCESS, MAIN_WINDOW_SHOWN} from 'common/communication';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import {getServerView, getTabViewName} from 'common/tabs/TabView';
|
||||
import urlUtils from 'common/utils/url';
|
||||
import {getTabViewName} from 'common/tabs/TabView';
|
||||
import {equalUrlsIgnoringSubpath} from 'common/utils/url';
|
||||
|
||||
import {MattermostView} from './MattermostView';
|
||||
import {ViewManager} from './viewManager';
|
||||
@@ -29,8 +29,8 @@ jest.mock('electron', () => ({
|
||||
}));
|
||||
|
||||
jest.mock('common/tabs/TabView', () => ({
|
||||
getServerView: jest.fn(),
|
||||
getTabViewName: jest.fn((a, b) => `${a}-${b}`),
|
||||
TAB_MESSAGING: 'tab',
|
||||
}));
|
||||
|
||||
jest.mock('common/servers/MattermostServer', () => ({
|
||||
@@ -45,7 +45,7 @@ jest.mock('common/utils/url', () => ({
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getView: jest.fn(),
|
||||
equalUrlsIgnoringSubpath: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('main/i18nManager', () => ({
|
||||
@@ -75,7 +75,7 @@ describe('main/views/viewManager', () => {
|
||||
beforeEach(() => {
|
||||
viewManager.createLoadingScreen = jest.fn();
|
||||
viewManager.showByName = jest.fn();
|
||||
getServerView.mockImplementation((srv, tab) => ({name: `${srv.name}-${tab.name}`}));
|
||||
viewManager.getServerView = jest.fn().mockImplementation((srv, tabName) => ({name: `${srv.name}-${tabName}`}));
|
||||
MattermostView.mockImplementation((tab) => ({
|
||||
on: jest.fn(),
|
||||
load: loadFn,
|
||||
@@ -184,9 +184,9 @@ describe('main/views/viewManager', () => {
|
||||
send: jest.fn(),
|
||||
};
|
||||
|
||||
getServerView.mockImplementation((srv, tab) => ({
|
||||
name: `${srv.name}-${tab.name}`,
|
||||
urlTypeTuple: tuple(`http://${srv.name}.com/`, tab.name),
|
||||
viewManager.getServerView = jest.fn().mockImplementation((srv, tabName) => ({
|
||||
name: `${srv.name}-${tabName}`,
|
||||
urlTypeTuple: tuple(`http://${srv.name}.com/`, tabName),
|
||||
url: new URL(`http://${srv.name}.com`),
|
||||
}));
|
||||
MattermostServer.mockImplementation((name, url) => ({
|
||||
@@ -684,6 +684,106 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getViewByURL', () => {
|
||||
const viewManager = new ViewManager({});
|
||||
viewManager.getServers = () => [
|
||||
{
|
||||
name: 'server-1',
|
||||
url: 'http://server-1.com',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
{
|
||||
name: 'tab-type1',
|
||||
},
|
||||
{
|
||||
name: 'tab-type2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'server-2',
|
||||
url: 'http://server-2.com/subpath',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab-type1',
|
||||
},
|
||||
{
|
||||
name: 'tab-type2',
|
||||
},
|
||||
{
|
||||
name: 'tab',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
viewManager.getServerView = (srv, tabName) => {
|
||||
const postfix = tabName.split('-')[1];
|
||||
return {
|
||||
name: `${srv.name}_${tabName}`,
|
||||
url: new URL(`${srv.url.toString().replace(/\/$/, '')}${postfix ? `/${postfix}` : ''}`),
|
||||
};
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
MattermostServer.mockImplementation((name, url) => ({
|
||||
name,
|
||||
url: new URL(url),
|
||||
}));
|
||||
equalUrlsIgnoringSubpath.mockImplementation((url1, url2) => `${url1}`.startsWith(`${url2}`));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should match the correct server - base URL', () => {
|
||||
const inputURL = new URL('http://server-1.com');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-1_tab', url: new URL('http://server-1.com')});
|
||||
});
|
||||
|
||||
it('should match the correct server - base tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/team');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-1_tab', url: new URL('http://server-1.com')});
|
||||
});
|
||||
|
||||
it('should match the correct server - different tab', () => {
|
||||
const inputURL = new URL('http://server-1.com/type1/app');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-1_tab-type1', url: new URL('http://server-1.com/type1')});
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL without', () => {
|
||||
const inputURL = new URL('http://server-2.com');
|
||||
expect(viewManager.getViewByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for server with subpath and URL with wrong subpath', () => {
|
||||
const inputURL = new URL('http://server-2.com/different/subpath');
|
||||
expect(viewManager.getViewByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base URL', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-2_tab', url: new URL('http://server-2.com/subpath')});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - base tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/team');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-2_tab', url: new URL('http://server-2.com/subpath')});
|
||||
});
|
||||
|
||||
it('should match the correct server with a subpath - different tab', () => {
|
||||
const inputURL = new URL('http://server-2.com/subpath/type2/team');
|
||||
expect(viewManager.getViewByURL(inputURL)).toStrictEqual({name: 'server-2_tab-type2', url: new URL('http://server-2.com/subpath/type2')});
|
||||
});
|
||||
|
||||
it('should return undefined for wrong server', () => {
|
||||
const inputURL = new URL('http://server-3.com');
|
||||
expect(viewManager.getViewByURL(inputURL)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleDeepLink', () => {
|
||||
const viewManager = new ViewManager({});
|
||||
const baseView = {
|
||||
@@ -705,6 +805,7 @@ describe('main/views/viewManager', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
viewManager.openClosedTab = jest.fn();
|
||||
viewManager.getViewByURL = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -714,7 +815,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should load URL into matching view', () => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
viewManager.getViewByURL.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
const view = {...baseView};
|
||||
viewManager.views.set('view1', view);
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
@@ -722,7 +823,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should send the URL to the view if its already loaded on a 6.0 server', () => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
viewManager.getViewByURL.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
const view = {
|
||||
...baseView,
|
||||
serverInfo: {
|
||||
@@ -743,7 +844,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should throw error if view is missing', () => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
viewManager.getViewByURL.mockImplementation(() => ({name: 'view1', url: 'http://server-1.com/'}));
|
||||
const view = {...baseView};
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
expect(view.load).not.toHaveBeenCalled();
|
||||
@@ -757,7 +858,7 @@ describe('main/views/viewManager', () => {
|
||||
});
|
||||
|
||||
it('should reopen closed tab if called upon', () => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'view1', url: 'https://server-1.com/'}));
|
||||
viewManager.getViewByURL.mockImplementation(() => ({name: 'view1', url: 'https://server-1.com/'}));
|
||||
viewManager.closedViews.set('view1', {});
|
||||
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
|
||||
expect(viewManager.openClosedTab).toHaveBeenCalledWith('view1', 'https://server-1.com/deep/link?thing=yes');
|
||||
|
@@ -23,10 +23,13 @@ import {
|
||||
MAIN_WINDOW_SHOWN,
|
||||
} from 'common/communication';
|
||||
import Config from 'common/config';
|
||||
import urlUtils from 'common/utils/url';
|
||||
import urlUtils, {equalUrlsIgnoringSubpath} from 'common/utils/url';
|
||||
import Utils from 'common/utils/util';
|
||||
import {MattermostServer} from 'common/servers/MattermostServer';
|
||||
import {getServerView, getTabViewName, TabTuple, TabType} from 'common/tabs/TabView';
|
||||
import {getTabViewName, TabTuple, TabType, TAB_FOCALBOARD, TAB_MESSAGING, TAB_PLAYBOOKS} from 'common/tabs/TabView';
|
||||
import MessagingTabView from 'common/tabs/MessagingTabView';
|
||||
import FocalboardTabView from 'common/tabs/FocalboardTabView';
|
||||
import PlaybooksTabView from 'common/tabs/PlaybooksTabView';
|
||||
|
||||
import {localizeMessage} from 'main/i18nManager';
|
||||
import {ServerInfo} from 'main/server/serverInfo';
|
||||
@@ -82,7 +85,7 @@ export class ViewManager {
|
||||
}
|
||||
|
||||
makeView = (srv: MattermostServer, serverInfo: ServerInfo, tab: Tab, url?: string): MattermostView => {
|
||||
const tabView = getServerView(srv, tab);
|
||||
const tabView = this.getServerView(srv, tab.name);
|
||||
const view = new MattermostView(tabView, serverInfo, this.mainWindow, this.viewOptions);
|
||||
view.once(LOAD_SUCCESS, this.activateView);
|
||||
view.load(url);
|
||||
@@ -146,10 +149,10 @@ export class ViewManager {
|
||||
for (const [team, tab] of sortedTabs) {
|
||||
const srv = new MattermostServer(team.name, team.url);
|
||||
const info = new ServerInfo(srv);
|
||||
const view = getServerView(srv, tab);
|
||||
const tabTuple = tuple(new URL(team.url).href, tab.name as TabType);
|
||||
const recycle = current.get(tabTuple);
|
||||
if (!tab.isOpen) {
|
||||
const view = this.getServerView(srv, tab.name);
|
||||
closed.set(tabTuple, {srv, tab, name: view.name});
|
||||
} else if (recycle) {
|
||||
recycle.updateServerInfo(srv);
|
||||
@@ -511,11 +514,38 @@ export class ViewManager {
|
||||
view.removeListener(LOAD_SUCCESS, this.deeplinkSuccess);
|
||||
}
|
||||
|
||||
getViewByURL = (inputURL: URL | string, ignoreScheme = false) => {
|
||||
log.silly('ViewManager.getViewByURL', `${inputURL}`, ignoreScheme);
|
||||
|
||||
const parsedURL = urlUtils.parseURL(inputURL);
|
||||
if (!parsedURL) {
|
||||
return undefined;
|
||||
}
|
||||
const server = this.getServers().find((team) => {
|
||||
const parsedServerUrl = urlUtils.parseURL(team.url)!;
|
||||
return equalUrlsIgnoringSubpath(parsedURL, parsedServerUrl, ignoreScheme) && parsedURL.pathname.match(new RegExp(`^${parsedServerUrl.pathname}(.+)?(/(.+))?$`));
|
||||
});
|
||||
if (!server) {
|
||||
return undefined;
|
||||
}
|
||||
const mmServer = new MattermostServer(server.name, server.url);
|
||||
let selectedTab = this.getServerView(mmServer, TAB_MESSAGING);
|
||||
server.tabs.
|
||||
filter((tab) => tab.name !== TAB_MESSAGING).
|
||||
forEach((tab) => {
|
||||
const tabCandidate = this.getServerView(mmServer, tab.name);
|
||||
if (parsedURL.pathname.match(new RegExp(`^${tabCandidate.url.pathname}(/(.+))?`))) {
|
||||
selectedTab = tabCandidate;
|
||||
}
|
||||
});
|
||||
return selectedTab;
|
||||
}
|
||||
|
||||
handleDeepLink = (url: string | URL) => {
|
||||
// TODO: fix for new tabs
|
||||
if (url) {
|
||||
const parsedURL = urlUtils.parseURL(url)!;
|
||||
const tabView = urlUtils.getView(parsedURL, this.getServers(), true);
|
||||
const tabView = this.getViewByURL(parsedURL, true);
|
||||
if (tabView) {
|
||||
const urlWithSchema = `${urlUtils.parseURL(tabView.url)?.origin}${parsedURL.pathname}${parsedURL.search}`;
|
||||
if (this.closedViews.has(tabView.name)) {
|
||||
@@ -555,4 +585,17 @@ export class ViewManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getServerView = (srv: MattermostServer, tabName: string) => {
|
||||
switch (tabName) {
|
||||
case TAB_MESSAGING:
|
||||
return new MessagingTabView(srv);
|
||||
case TAB_FOCALBOARD:
|
||||
return new FocalboardTabView(srv);
|
||||
case TAB_PLAYBOOKS:
|
||||
return new PlaybooksTabView(srv);
|
||||
default:
|
||||
throw new Error('Not implemeneted');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ jest.mock('electron', () => ({
|
||||
|
||||
jest.mock('../allowProtocolDialog', () => ({}));
|
||||
jest.mock('../windows/windowManager', () => ({
|
||||
getServerURLFromWebContentsId: jest.fn(),
|
||||
showMainWindow: jest.fn(),
|
||||
}));
|
||||
|
||||
@@ -83,7 +84,7 @@ describe('main/views/webContentsEvents', () => {
|
||||
const willNavigate = webContentsEventManager.generateWillNavigate(jest.fn());
|
||||
|
||||
beforeEach(() => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'server_name', url: 'http://server-1.com'}));
|
||||
WindowManager.getServerURLFromWebContentsId.mockImplementation(() => new URL('http://server-1.com'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -100,7 +101,7 @@ describe('main/views/webContentsEvents', () => {
|
||||
});
|
||||
|
||||
it('should allow navigation when url isAdminURL', () => {
|
||||
urlUtils.isAdminUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}/admin_console`));
|
||||
urlUtils.isAdminUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}admin_console`));
|
||||
willNavigate(event, 'http://server-1.com/admin_console/subpath');
|
||||
expect(event.preventDefault).not.toBeCalled();
|
||||
});
|
||||
@@ -146,7 +147,7 @@ describe('main/views/webContentsEvents', () => {
|
||||
const didStartNavigation = webContentsEventManager.generateDidStartNavigation(jest.fn());
|
||||
|
||||
beforeEach(() => {
|
||||
urlUtils.getView.mockImplementation(() => ({name: 'server_name', url: 'http://server-1.com'}));
|
||||
WindowManager.getServerURLFromWebContentsId.mockImplementation(() => new URL('http://server-1.com'));
|
||||
urlUtils.isTrustedURL.mockReturnValue(true);
|
||||
urlUtils.isInternalURL.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(serverURL));
|
||||
urlUtils.isCustomLoginURL.mockImplementation((parsedURL) => parsedURL.toString().startsWith('http://loginurl.com/login'));
|
||||
@@ -176,11 +177,11 @@ describe('main/views/webContentsEvents', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
urlUtils.isValidURI.mockReturnValue(true);
|
||||
urlUtils.getView.mockReturnValue({name: 'server_name', url: 'http://server-1.com'});
|
||||
urlUtils.isTeamUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}/myteam`));
|
||||
urlUtils.isAdminUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}/admin_console`));
|
||||
urlUtils.isPluginUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}/myplugin`));
|
||||
urlUtils.isManagedResource.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}/myplugin`));
|
||||
WindowManager.getServerURLFromWebContentsId.mockImplementation(() => new URL('http://server-1.com'));
|
||||
urlUtils.isTeamUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}myteam`));
|
||||
urlUtils.isAdminUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}admin_console`));
|
||||
urlUtils.isPluginUrl.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}myplugin`));
|
||||
urlUtils.isManagedResource.mockImplementation((serverURL, parsedURL) => parsedURL.toString().startsWith(`${serverURL}myplugin`));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -211,7 +212,7 @@ describe('main/views/webContentsEvents', () => {
|
||||
});
|
||||
|
||||
it('should open in the browser when there is no server matching', () => {
|
||||
urlUtils.getView.mockReturnValue(null);
|
||||
WindowManager.getServerURLFromWebContentsId.mockReturnValue(undefined);
|
||||
expect(newWindow({url: 'http://server-2.com/subpath'})).toStrictEqual({action: 'deny'});
|
||||
expect(shell.openExternal).toBeCalledWith('http://server-2.com/subpath');
|
||||
});
|
||||
|
@@ -37,7 +37,7 @@ export class WebContentsEventManager {
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
isTrustedPopupWindow = (webContents: WebContents) => {
|
||||
private isTrustedPopupWindow = (webContents: WebContents) => {
|
||||
if (!webContents) {
|
||||
return false;
|
||||
}
|
||||
@@ -47,24 +47,23 @@ export class WebContentsEventManager {
|
||||
return BrowserWindow.fromWebContents(webContents) === this.popupWindow;
|
||||
}
|
||||
|
||||
generateWillNavigate = (getServersFunction: () => TeamWithTabs[]) => {
|
||||
generateWillNavigate = () => {
|
||||
return (event: Event & {sender: WebContents}, url: string) => {
|
||||
log.debug('webContentEvents.will-navigate', {webContentsId: event.sender.id, url});
|
||||
|
||||
const contentID = event.sender.id;
|
||||
const parsedURL = urlUtils.parseURL(url)!;
|
||||
const configServers = getServersFunction();
|
||||
const server = urlUtils.getView(parsedURL, configServers);
|
||||
const serverURL = WindowManager.getServerURLFromWebContentsId(event.sender.id);
|
||||
|
||||
if (server && (urlUtils.isTeamUrl(server.url, parsedURL) || urlUtils.isAdminUrl(server.url, parsedURL) || this.isTrustedPopupWindow(event.sender))) {
|
||||
if (serverURL && (urlUtils.isTeamUrl(serverURL, parsedURL) || urlUtils.isAdminUrl(serverURL, parsedURL) || this.isTrustedPopupWindow(event.sender))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (server && urlUtils.isChannelExportUrl(server.url, parsedURL)) {
|
||||
if (serverURL && urlUtils.isChannelExportUrl(serverURL, parsedURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (server && urlUtils.isCustomLoginURL(parsedURL, server, configServers)) {
|
||||
if (serverURL && urlUtils.isCustomLoginURL(parsedURL, serverURL)) {
|
||||
return;
|
||||
}
|
||||
if (parsedURL.protocol === 'mailto:') {
|
||||
@@ -80,24 +79,21 @@ export class WebContentsEventManager {
|
||||
};
|
||||
};
|
||||
|
||||
generateDidStartNavigation = (getServersFunction: () => TeamWithTabs[]) => {
|
||||
generateDidStartNavigation = () => {
|
||||
return (event: Event & {sender: WebContents}, url: string) => {
|
||||
log.debug('webContentEvents.did-start-navigation', {webContentsId: event.sender.id, url});
|
||||
|
||||
const serverList = getServersFunction();
|
||||
const contentID = event.sender.id;
|
||||
const parsedURL = urlUtils.parseURL(url)!;
|
||||
const server = urlUtils.getView(parsedURL, serverList);
|
||||
const serverURL = WindowManager.getServerURLFromWebContentsId(event.sender.id);
|
||||
|
||||
if (!urlUtils.isTrustedURL(parsedURL, serverList)) {
|
||||
if (!serverURL || !urlUtils.isTrustedURL(parsedURL, serverURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverURL = urlUtils.parseURL(server?.url || '');
|
||||
|
||||
if (server && urlUtils.isCustomLoginURL(parsedURL, server, serverList)) {
|
||||
if (serverURL && urlUtils.isCustomLoginURL(parsedURL, serverURL)) {
|
||||
this.customLogins[contentID].inProgress = true;
|
||||
} else if (server && this.customLogins[contentID].inProgress && urlUtils.isInternalURL(serverURL || new URL(''), parsedURL)) {
|
||||
} else if (serverURL && this.customLogins[contentID].inProgress && urlUtils.isInternalURL(serverURL || new URL(''), parsedURL)) {
|
||||
this.customLogins[contentID].inProgress = false;
|
||||
}
|
||||
};
|
||||
@@ -108,7 +104,7 @@ export class WebContentsEventManager {
|
||||
return {action: 'deny'};
|
||||
};
|
||||
|
||||
generateNewWindowListener = (getServersFunction: () => TeamWithTabs[], spellcheck?: boolean) => {
|
||||
generateNewWindowListener = (webContentsId: number, spellcheck?: boolean) => {
|
||||
return (details: Electron.HandlerDetails): {action: 'deny' | 'allow'} => {
|
||||
log.debug('webContentEvents.new-window', details.url);
|
||||
|
||||
@@ -118,8 +114,6 @@ export class WebContentsEventManager {
|
||||
return {action: 'deny'};
|
||||
}
|
||||
|
||||
const configServers = getServersFunction();
|
||||
|
||||
// Dev tools case
|
||||
if (parsedURL.protocol === 'devtools:') {
|
||||
return {action: 'allow'};
|
||||
@@ -138,9 +132,9 @@ export class WebContentsEventManager {
|
||||
return {action: 'deny'};
|
||||
}
|
||||
|
||||
const server = urlUtils.getView(parsedURL, configServers);
|
||||
const serverURL = WindowManager.getServerURLFromWebContentsId(webContentsId);
|
||||
|
||||
if (!server) {
|
||||
if (!serverURL) {
|
||||
shell.openExternal(details.url);
|
||||
return {action: 'deny'};
|
||||
}
|
||||
@@ -166,11 +160,11 @@ export class WebContentsEventManager {
|
||||
return {action: 'deny'};
|
||||
}
|
||||
|
||||
if (urlUtils.isTeamUrl(server.url, parsedURL, true)) {
|
||||
if (urlUtils.isTeamUrl(serverURL, parsedURL, true)) {
|
||||
WindowManager.showMainWindow(parsedURL);
|
||||
return {action: 'deny'};
|
||||
}
|
||||
if (urlUtils.isAdminUrl(server.url, parsedURL)) {
|
||||
if (urlUtils.isAdminUrl(serverURL, parsedURL)) {
|
||||
log.info(`${details.url} is an admin console page, preventing to open a new window`);
|
||||
return {action: 'deny'};
|
||||
}
|
||||
@@ -180,7 +174,7 @@ export class WebContentsEventManager {
|
||||
}
|
||||
|
||||
// TODO: move popups to its own and have more than one.
|
||||
if (urlUtils.isPluginUrl(server.url, parsedURL) || urlUtils.isManagedResource(server.url, parsedURL)) {
|
||||
if (urlUtils.isPluginUrl(serverURL, parsedURL) || urlUtils.isManagedResource(serverURL, parsedURL)) {
|
||||
if (!this.popupWindow) {
|
||||
this.popupWindow = new BrowserWindow({
|
||||
backgroundColor: '#fff', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
||||
@@ -200,7 +194,7 @@ export class WebContentsEventManager {
|
||||
});
|
||||
}
|
||||
|
||||
if (urlUtils.isManagedResource(server.url, parsedURL)) {
|
||||
if (urlUtils.isManagedResource(serverURL, parsedURL)) {
|
||||
this.popupWindow.loadURL(details.url);
|
||||
} else {
|
||||
// currently changing the userAgent for popup windows to allow plugins to go through google's oAuth
|
||||
@@ -258,7 +252,7 @@ export class WebContentsEventManager {
|
||||
this.removeWebContentsListeners(contents.id);
|
||||
}
|
||||
|
||||
const willNavigate = this.generateWillNavigate(getServersFunction);
|
||||
const willNavigate = this.generateWillNavigate();
|
||||
contents.on('will-navigate', willNavigate as (e: Event, u: string) => void); // TODO: Electron types don't include sender for some reason
|
||||
|
||||
// handle custom login requests (oath, saml):
|
||||
@@ -266,11 +260,11 @@ export class WebContentsEventManager {
|
||||
// - indicate custom login is in progress
|
||||
// 2. are we finished with the custom login process?
|
||||
// - indicate custom login is NOT in progress
|
||||
const didStartNavigation = this.generateDidStartNavigation(getServersFunction);
|
||||
const didStartNavigation = this.generateDidStartNavigation();
|
||||
contents.on('did-start-navigation', didStartNavigation as (e: Event, u: string) => void);
|
||||
|
||||
const spellcheck = Config.useSpellChecker;
|
||||
const newWindow = this.generateNewWindowListener(getServersFunction, spellcheck);
|
||||
const newWindow = this.generateNewWindowListener(contents.id, spellcheck);
|
||||
contents.setWindowOpenHandler(newWindow);
|
||||
|
||||
addListeners?.(contents);
|
||||
|
@@ -47,7 +47,6 @@ jest.mock('common/config', () => ({}));
|
||||
jest.mock('common/utils/url', () => ({
|
||||
isTeamUrl: jest.fn(),
|
||||
isAdminUrl: jest.fn(),
|
||||
getView: jest.fn(),
|
||||
cleanPathName: jest.fn(),
|
||||
}));
|
||||
jest.mock('common/tabs/TabView', () => ({
|
||||
@@ -874,6 +873,7 @@ describe('main/windows/windowManager', () => {
|
||||
]),
|
||||
openClosedTab: jest.fn(),
|
||||
showByName: jest.fn(),
|
||||
getViewByURL: jest.fn(),
|
||||
};
|
||||
windowManager.handleBrowserHistoryButton = jest.fn();
|
||||
|
||||
@@ -911,7 +911,7 @@ describe('main/windows/windowManager', () => {
|
||||
});
|
||||
|
||||
it('should open closed view if pushing to it', () => {
|
||||
urlUtils.getView.mockReturnValue({name: 'server-1_other_type_2'});
|
||||
windowManager.viewManager.getViewByURL.mockReturnValue({name: 'server-1_other_type_2'});
|
||||
windowManager.viewManager.openClosedTab.mockImplementation((name) => {
|
||||
const view = windowManager.viewManager.closedViews.get(name);
|
||||
windowManager.viewManager.closedViews.delete(name);
|
||||
@@ -923,13 +923,13 @@ describe('main/windows/windowManager', () => {
|
||||
});
|
||||
|
||||
it('should open redirect view if different from current view', () => {
|
||||
urlUtils.getView.mockReturnValue({name: 'server-1_other_type_1'});
|
||||
windowManager.viewManager.getViewByURL.mockReturnValue({name: 'server-1_other_type_1'});
|
||||
windowManager.handleBrowserHistoryPush(null, 'server-1_tab-messaging', '/other_type_1/subpath');
|
||||
expect(windowManager.viewManager.showByName).toBeCalledWith('server-1_other_type_1');
|
||||
});
|
||||
|
||||
it('should ignore redirects to "/" to Messages from other tabs', () => {
|
||||
urlUtils.getView.mockReturnValue({name: 'server-1_tab-messaging'});
|
||||
windowManager.viewManager.getViewByURL.mockReturnValue({name: 'server-1_tab-messaging'});
|
||||
windowManager.handleBrowserHistoryPush(null, 'server-1_other_type_1', '/');
|
||||
expect(view1.view.webContents.send).not.toBeCalled();
|
||||
});
|
||||
|
@@ -752,7 +752,7 @@ export class WindowManager {
|
||||
|
||||
const currentView = this.viewManager?.views.get(viewName);
|
||||
const cleanedPathName = urlUtils.cleanPathName(currentView?.tab.server.url.pathname || '', pathName);
|
||||
const redirectedViewName = urlUtils.getView(`${currentView?.tab.server.url}${cleanedPathName}`, Config.teams)?.name || viewName;
|
||||
const redirectedViewName = this.viewManager?.getViewByURL(`${currentView?.tab.server.url.toString().replace(/\/$/, '')}${cleanedPathName}`)?.name || viewName;
|
||||
if (this.viewManager?.closedViews.has(redirectedViewName)) {
|
||||
// If it's a closed view, just open it and stop
|
||||
this.viewManager.openClosedTab(redirectedViewName, `${currentView?.tab.server.url}${cleanedPathName}`);
|
||||
@@ -857,6 +857,14 @@ export class WindowManager {
|
||||
view?.reload();
|
||||
this.viewManager?.showByName(view?.name);
|
||||
}
|
||||
|
||||
getServerURLFromWebContentsId = (id: number) => {
|
||||
const viewName = this.getViewNameByWebContentsId(id);
|
||||
if (!viewName) {
|
||||
return undefined;
|
||||
}
|
||||
return this.viewManager?.views.get(viewName)?.tab.server.url;
|
||||
}
|
||||
}
|
||||
|
||||
const windowManager = new WindowManager();
|
||||
|
Reference in New Issue
Block a user