mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-12-22 10:08:55 +00:00
Update to Electron 25 (#1559)
This is intended to get Electron updated to 25. There are no known bugs in this release. As well this includes a fix for an existing bug I noticed where child windows in Windows received a menu bar when they should not have.
This commit is contained in:
parent
be418d4349
commit
64157c3c5d
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1 # Setup .npmrc file to publish to npm
|
- uses: actions/setup-node@v2 # Setup .npmrc file to publish to npm
|
||||||
with:
|
with:
|
||||||
node-version: '20' # Align the version of Node here with ci.yml.
|
node-version: '20' # Align the version of Node here with ci.yml.
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1 # Setup .npmrc file to publish to npm
|
- uses: actions/setup-node@v2 # Setup .npmrc file to publish to npm
|
||||||
with:
|
with:
|
||||||
node-version: '20' # Align the version of Node here with ci.yml.
|
node-version: '20' # Align the version of Node here with ci.yml.
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -44,7 +44,6 @@ build/Release
|
|||||||
# Dependency directory
|
# Dependency directory
|
||||||
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
||||||
node_modules
|
node_modules
|
||||||
.nvmrc
|
|
||||||
|
|
||||||
# Python virtual environment in case it's created for the Castlabs code signing tool
|
# Python virtual environment in case it's created for the Castlabs code signing tool
|
||||||
venv
|
venv
|
||||||
|
1333
app/npm-shrinkwrap.json
generated
1333
app/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,14 +12,14 @@
|
|||||||
],
|
],
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-context-menu": "^3.1.1",
|
"electron-context-menu": "^3.6.1",
|
||||||
"electron-dl": "^3.2.1",
|
"electron-dl": "^3.5.0",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"electron-window-state": "^5.0.3",
|
"electron-window-state": "^5.0.3",
|
||||||
"loglevel": "^1.7.1",
|
"loglevel": "^1.8.1",
|
||||||
"source-map-support": "^0.5.19"
|
"source-map-support": "^0.5.21"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^21.4.4"
|
"electron": "^25.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
ContextMenuParams,
|
ContextMenuParams,
|
||||||
NewWindowWebContentsEvent,
|
Event as ElectronEvent,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import contextMenu from 'electron-context-menu';
|
import contextMenu from 'electron-context-menu';
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export function initContextMenu(
|
|||||||
prepend: (actions: contextMenu.Actions, params: ContextMenuParams) => {
|
prepend: (actions: contextMenu.Actions, params: ContextMenuParams) => {
|
||||||
log.debug('contextMenu.prepend', { actions, params });
|
log.debug('contextMenu.prepend', { actions, params });
|
||||||
const items = [];
|
const items = [];
|
||||||
if (params.linkURL) {
|
if (params.linkURL && window) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Open Link in Default Browser',
|
label: 'Open Link in Default Browser',
|
||||||
click: () => {
|
click: () => {
|
||||||
@ -38,36 +38,40 @@ export function initContextMenu(
|
|||||||
label: 'Open Link in New Window',
|
label: 'Open Link in New Window',
|
||||||
click: () =>
|
click: () =>
|
||||||
createNewWindow(
|
createNewWindow(
|
||||||
outputOptionsToWindowOptions(options),
|
outputOptionsToWindowOptions(options, nativeTabsSupported()),
|
||||||
setupNativefierWindow,
|
setupNativefierWindow,
|
||||||
params.linkURL,
|
params.linkURL,
|
||||||
window,
|
// window,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
if (nativeTabsSupported()) {
|
if (nativeTabsSupported()) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Open Link in New Tab',
|
label: 'Open Link in New Tab',
|
||||||
click: () =>
|
click: () =>
|
||||||
// Fire a new window event for a foreground tab
|
// // Fire a new window event for a foreground tab
|
||||||
// Previously we called createNewTab directly, but it had incosistent and buggy behavior
|
// // Previously we called createNewTab directly, but it had incosistent and buggy behavior
|
||||||
// as it was mostly designed for running off of events. So this will create a new event
|
// // as it was mostly designed for running off of events. So this will create a new event
|
||||||
// for a foreground-tab for the event handler to grab and take care of instead.
|
// // for a foreground-tab for the event handler to grab and take care of instead.
|
||||||
(window as BrowserWindow).webContents.emit(
|
// (window as BrowserWindow).webContents.emit(
|
||||||
// event name
|
// // event name
|
||||||
'new-window',
|
// 'new-window',
|
||||||
// event object
|
// // event object
|
||||||
{
|
// {
|
||||||
// Leave to the default for a NewWindowWebContentsEvent
|
// // Leave to the default for a NewWindowWebContentsEvent
|
||||||
newGuest: undefined,
|
// newGuest: undefined,
|
||||||
...new Event('new-window'),
|
// ...new Event('new-window'),
|
||||||
} as NewWindowWebContentsEvent,
|
// }, // as NewWindowWebContentsEvent,
|
||||||
// url
|
// // url
|
||||||
params.linkURL,
|
// params.linkURL,
|
||||||
// frameName
|
// // frameName
|
||||||
window?.webContents.mainFrame.name ?? '',
|
// window?.webContents.mainFrame.name ?? '',
|
||||||
// disposition
|
// // disposition
|
||||||
'foreground-tab',
|
// 'foreground-tab',
|
||||||
),
|
// ),
|
||||||
|
window.emit('new-window-for-tab', {
|
||||||
|
...new Event('new-window-for-tab'),
|
||||||
|
url: params.linkURL,
|
||||||
|
} as ElectronEvent<{ url: string }>),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,19 @@ import * as path from 'path';
|
|||||||
import { BrowserWindow, ipcMain } from 'electron';
|
import { BrowserWindow, ipcMain } from 'electron';
|
||||||
|
|
||||||
import * as log from '../helpers/loggingHelper';
|
import * as log from '../helpers/loggingHelper';
|
||||||
|
import { nativeTabsSupported } from '../helpers/helpers';
|
||||||
|
|
||||||
export async function createLoginWindow(
|
export async function createLoginWindow(
|
||||||
loginCallback: (username?: string, password?: string) => void,
|
loginCallback: (username?: string, password?: string) => void,
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
): Promise<BrowserWindow> {
|
): Promise<BrowserWindow> {
|
||||||
log.debug('createLoginWindow', { loginCallback, parent });
|
log.debug('createLoginWindow', {
|
||||||
|
loginCallback,
|
||||||
|
parent,
|
||||||
|
});
|
||||||
|
|
||||||
const loginWindow = new BrowserWindow({
|
const loginWindow = new BrowserWindow({
|
||||||
parent,
|
parent: nativeTabsSupported() ? undefined : parent,
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 400,
|
height: 400,
|
||||||
frame: false,
|
frame: false,
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { ipcMain, desktopCapturer, BrowserWindow, Event } from 'electron';
|
import {
|
||||||
|
desktopCapturer,
|
||||||
|
ipcMain,
|
||||||
|
BrowserWindow,
|
||||||
|
Event,
|
||||||
|
HandlerDetails,
|
||||||
|
} from 'electron';
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from 'electron-window-state';
|
||||||
|
|
||||||
import { initContextMenu } from './contextMenu';
|
import { initContextMenu } from './contextMenu';
|
||||||
@ -36,9 +42,9 @@ type SessionInteractionRequest = {
|
|||||||
propertyValue?: unknown;
|
propertyValue?: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SessionInteractionResult = {
|
type SessionInteractionResult<T = unknown> = {
|
||||||
id?: string;
|
id?: string;
|
||||||
value?: unknown | Promise<unknown>;
|
value?: T | Promise<T>;
|
||||||
error?: Error;
|
error?: Error;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,7 +84,9 @@ export async function createMainWindow(
|
|||||||
// So, we manually mainWindow.show() later, see a few lines below
|
// So, we manually mainWindow.show() later, see a few lines below
|
||||||
show: options.tray !== 'start-in-tray' && process.platform !== 'win32',
|
show: options.tray !== 'start-in-tray' && process.platform !== 'win32',
|
||||||
backgroundColor: options.backgroundColor,
|
backgroundColor: options.backgroundColor,
|
||||||
...getDefaultWindowOptions(outputOptionsToWindowOptions(options)),
|
...getDefaultWindowOptions(
|
||||||
|
outputOptionsToWindowOptions(options, nativeTabsSupported()),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Just load about:blank to start, gives playwright something to latch onto initially for testing.
|
// Just load about:blank to start, gives playwright something to latch onto initially for testing.
|
||||||
@ -102,44 +110,32 @@ export async function createMainWindow(
|
|||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowOptions = outputOptionsToWindowOptions(options);
|
const windowOptions = outputOptionsToWindowOptions(
|
||||||
|
options,
|
||||||
|
nativeTabsSupported(),
|
||||||
|
);
|
||||||
createMenu(options, mainWindow);
|
createMenu(options, mainWindow);
|
||||||
createContextMenu(options, mainWindow);
|
createContextMenu(options, mainWindow);
|
||||||
setupNativefierWindow(windowOptions, mainWindow);
|
setupNativefierWindow(windowOptions, mainWindow);
|
||||||
|
|
||||||
// .on('new-window', ...) is deprected in favor of setWindowOpenHandler(...)
|
|
||||||
// We can't quite cut over to that yet for a few reasons:
|
|
||||||
// 1. Our version of Electron does not yet support a parameter to
|
|
||||||
// setWindowOpenHandler that contains `disposition', which we need.
|
|
||||||
// See https://github.com/electron/electron/issues/28380
|
|
||||||
// 2. setWindowOpenHandler doesn't support newGuest as well
|
|
||||||
// Though at this point, 'new-window' bugs seem to be coming up and downstream
|
|
||||||
// users are being pointed to use setWindowOpenHandler.
|
|
||||||
// E.g., https://github.com/electron/electron/issues/28374
|
|
||||||
|
|
||||||
// Note it is important to add these handlers only to the *main* window,
|
// Note it is important to add these handlers only to the *main* window,
|
||||||
// else we run into weird behavior like opening tabs twice
|
// else we run into weird behavior like opening tabs twice
|
||||||
mainWindow.webContents.on(
|
mainWindow.webContents.setWindowOpenHandler((details: HandlerDetails) => {
|
||||||
'new-window',
|
return onNewWindow(
|
||||||
(event, url, frameName, disposition) => {
|
windowOptions,
|
||||||
onNewWindow(
|
setupNativefierWindow,
|
||||||
windowOptions,
|
details,
|
||||||
setupNativefierWindow,
|
mainWindow,
|
||||||
event,
|
);
|
||||||
url,
|
});
|
||||||
frameName,
|
mainWindow.on('new-window-for-tab', (event?: Event<{ url?: string }>) => {
|
||||||
disposition,
|
log.debug('mainWindow.new-window-for-tab', { event });
|
||||||
).catch((err) => log.error('onNewWindow ERROR', err));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// @ts-expect-error new-tab isn't in the type definition, but it does exist
|
|
||||||
mainWindow.on('new-tab', () => {
|
|
||||||
createNewTab(
|
createNewTab(
|
||||||
windowOptions,
|
windowOptions,
|
||||||
setupNativefierWindow,
|
setupNativefierWindow,
|
||||||
options.targetUrl,
|
event?.url ?? options.targetUrl,
|
||||||
true,
|
true,
|
||||||
mainWindow,
|
// mainWindow,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -154,7 +150,7 @@ export async function createMainWindow(
|
|||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
setupSessionInteraction(options, mainWindow);
|
setupSessionInteraction(mainWindow);
|
||||||
setupSessionPermissionHandler(mainWindow);
|
setupSessionPermissionHandler(mainWindow);
|
||||||
|
|
||||||
if (options.clearCache) {
|
if (options.clearCache) {
|
||||||
@ -265,10 +261,7 @@ function setupNotificationBadge(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupSessionInteraction(
|
function setupSessionInteraction(window: BrowserWindow): void {
|
||||||
options: OutputOptions,
|
|
||||||
window: BrowserWindow,
|
|
||||||
): void {
|
|
||||||
// See API.md / "Accessing The Electron Session"
|
// See API.md / "Accessing The Electron Session"
|
||||||
ipcMain.on(
|
ipcMain.on(
|
||||||
'session-interaction',
|
'session-interaction',
|
||||||
|
@ -2,7 +2,7 @@ jest.mock('./helpers');
|
|||||||
jest.mock('./windowEvents');
|
jest.mock('./windowEvents');
|
||||||
jest.mock('./windowHelpers');
|
jest.mock('./windowHelpers');
|
||||||
|
|
||||||
import { dialog, BrowserWindow } from 'electron';
|
import { dialog, BrowserWindow, HandlerDetails, WebContents } from 'electron';
|
||||||
import { WindowOptions } from '../../../shared/src/options/model';
|
import { WindowOptions } from '../../../shared/src/options/model';
|
||||||
import { linkIsInternal, openExternal, nativeTabsSupported } from './helpers';
|
import { linkIsInternal, openExternal, nativeTabsSupported } from './helpers';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
@ -14,11 +14,9 @@ const {
|
|||||||
onNewWindowHelper: (
|
onNewWindowHelper: (
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
||||||
urlToGo: string,
|
details: Partial<HandlerDetails>,
|
||||||
disposition: string | undefined,
|
|
||||||
preventDefault: (newGuest: BrowserWindow) => void,
|
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
) => Promise<void>;
|
) => ReturnType<Parameters<WebContents['setWindowOpenHandler']>[0]>;
|
||||||
onWillNavigate: (
|
onWillNavigate: (
|
||||||
options: {
|
options: {
|
||||||
blockExternalUrls: boolean;
|
blockExternalUrls: boolean;
|
||||||
@ -43,12 +41,13 @@ describe('onNewWindowHelper', () => {
|
|||||||
const foregroundDisposition = 'foreground-tab';
|
const foregroundDisposition = 'foreground-tab';
|
||||||
const backgroundDisposition = 'background-tab';
|
const backgroundDisposition = 'background-tab';
|
||||||
const baseOptions = {
|
const baseOptions = {
|
||||||
|
autoHideMenuBar: true,
|
||||||
blockExternalUrls: false,
|
blockExternalUrls: false,
|
||||||
insecure: false,
|
insecure: false,
|
||||||
name: 'TEST_APP',
|
name: 'TEST_APP',
|
||||||
targetUrl: originalURL,
|
targetUrl: originalURL,
|
||||||
zoom: 1.0,
|
zoom: 1.0,
|
||||||
};
|
} as WindowOptions;
|
||||||
const mockShowNavigationBlockedMessage: jest.SpyInstance =
|
const mockShowNavigationBlockedMessage: jest.SpyInstance =
|
||||||
showNavigationBlockedMessage as jest.Mock;
|
showNavigationBlockedMessage as jest.Mock;
|
||||||
const mockCreateAboutBlank: jest.SpyInstance =
|
const mockCreateAboutBlank: jest.SpyInstance =
|
||||||
@ -60,7 +59,6 @@ describe('onNewWindowHelper', () => {
|
|||||||
const mockNativeTabsSupported: jest.SpyInstance =
|
const mockNativeTabsSupported: jest.SpyInstance =
|
||||||
nativeTabsSupported as jest.Mock;
|
nativeTabsSupported as jest.Mock;
|
||||||
const mockOpenExternal: jest.SpyInstance = openExternal as jest.Mock;
|
const mockOpenExternal: jest.SpyInstance = openExternal as jest.Mock;
|
||||||
const preventDefault = jest.fn();
|
|
||||||
const setupWindow = jest.fn();
|
const setupWindow = jest.fn();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -72,7 +70,6 @@ describe('onNewWindowHelper', () => {
|
|||||||
mockLinkIsInternal.mockReset().mockReturnValue(true);
|
mockLinkIsInternal.mockReset().mockReturnValue(true);
|
||||||
mockNativeTabsSupported.mockReset().mockReturnValue(false);
|
mockNativeTabsSupported.mockReset().mockReturnValue(false);
|
||||||
mockOpenExternal.mockReset();
|
mockOpenExternal.mockReset();
|
||||||
preventDefault.mockReset();
|
|
||||||
setupWindow.mockReset();
|
setupWindow.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,105 +82,84 @@ describe('onNewWindowHelper', () => {
|
|||||||
mockOpenExternal.mockRestore();
|
mockOpenExternal.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('internal urls should not be handled', async () => {
|
test('internal urls should not be handled', () => {
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: internalURL,
|
||||||
setupWindow,
|
});
|
||||||
internalURL,
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).not.toHaveBeenCalled();
|
expect(result.action).toEqual('allow');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('external urls should be opened externally', async () => {
|
test('external urls should be opened externally', () => {
|
||||||
mockLinkIsInternal.mockReturnValue(false);
|
mockLinkIsInternal.mockReturnValue(false);
|
||||||
|
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: externalURL,
|
||||||
setupWindow,
|
});
|
||||||
externalURL,
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).toHaveBeenCalledTimes(1);
|
expect(mockOpenExternal).toHaveBeenCalledTimes(1);
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('external urls should be ignored if blockExternalUrls is true', async () => {
|
test('external urls should be ignored if blockExternalUrls is true', () => {
|
||||||
mockLinkIsInternal.mockReturnValue(false);
|
mockLinkIsInternal.mockReturnValue(false);
|
||||||
const options = {
|
const options = {
|
||||||
...baseOptions,
|
...baseOptions,
|
||||||
blockExternalUrls: true,
|
blockExternalUrls: true,
|
||||||
};
|
};
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(options, setupWindow, {
|
||||||
options,
|
url: externalURL,
|
||||||
setupWindow,
|
});
|
||||||
externalURL,
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).toHaveBeenCalledTimes(1);
|
expect(mockShowNavigationBlockedMessage).toHaveBeenCalledTimes(1);
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('tab disposition should be ignored if tabs are not enabled', async () => {
|
test('tab disposition should be ignored if tabs are not enabled', () => {
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: internalURL,
|
||||||
setupWindow,
|
disposition: foregroundDisposition,
|
||||||
internalURL,
|
});
|
||||||
foregroundDisposition,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).not.toHaveBeenCalled();
|
expect(result.action).toEqual('allow');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('tab disposition should be ignored if url is external', async () => {
|
test('tab disposition should be ignored if url is external', () => {
|
||||||
mockLinkIsInternal.mockReturnValue(false);
|
mockLinkIsInternal.mockReturnValue(false);
|
||||||
|
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: externalURL,
|
||||||
setupWindow,
|
disposition: foregroundDisposition,
|
||||||
externalURL,
|
});
|
||||||
foregroundDisposition,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).toHaveBeenCalledTimes(1);
|
expect(mockOpenExternal).toHaveBeenCalledTimes(1);
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('foreground tabs with internal urls should be opened in the foreground', async () => {
|
test('foreground tabs with internal urls should be opened in the foreground', () => {
|
||||||
mockNativeTabsSupported.mockReturnValue(true);
|
mockNativeTabsSupported.mockReturnValue(true);
|
||||||
|
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: internalURL,
|
||||||
setupWindow,
|
disposition: foregroundDisposition,
|
||||||
internalURL,
|
});
|
||||||
foregroundDisposition,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).toHaveBeenCalledTimes(1);
|
expect(mockCreateNewTab).toHaveBeenCalledTimes(1);
|
||||||
@ -192,23 +168,19 @@ describe('onNewWindowHelper', () => {
|
|||||||
setupWindow,
|
setupWindow,
|
||||||
internalURL,
|
internalURL,
|
||||||
true,
|
true,
|
||||||
undefined,
|
|
||||||
);
|
);
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('background tabs with internal urls should be opened in background tabs', async () => {
|
test('background tabs with internal urls should be opened in background tabs', () => {
|
||||||
mockNativeTabsSupported.mockReturnValue(true);
|
mockNativeTabsSupported.mockReturnValue(true);
|
||||||
|
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: internalURL,
|
||||||
setupWindow,
|
disposition: backgroundDisposition,
|
||||||
internalURL,
|
});
|
||||||
backgroundDisposition,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).toHaveBeenCalledTimes(1);
|
expect(mockCreateNewTab).toHaveBeenCalledTimes(1);
|
||||||
@ -217,59 +189,46 @@ describe('onNewWindowHelper', () => {
|
|||||||
setupWindow,
|
setupWindow,
|
||||||
internalURL,
|
internalURL,
|
||||||
false,
|
false,
|
||||||
undefined,
|
|
||||||
);
|
);
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('about:blank urls should be handled', async () => {
|
test('about:blank urls should be handled', () => {
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: 'about:blank',
|
||||||
setupWindow,
|
});
|
||||||
'about:blank',
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).toHaveBeenCalledTimes(1);
|
expect(mockCreateAboutBlank).toHaveBeenCalledTimes(1);
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('about:blank#blocked urls should be handled', async () => {
|
test('about:blank#blocked urls should be handled', () => {
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: 'about:blank#blocked',
|
||||||
setupWindow,
|
});
|
||||||
'about:blank#blocked',
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).toHaveBeenCalledTimes(1);
|
expect(mockCreateAboutBlank).toHaveBeenCalledTimes(1);
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
expect(result.action).toEqual('deny');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('about:blank#other urls should not be handled', async () => {
|
test('about:blank#other urls should not be handled', () => {
|
||||||
await onNewWindowHelper(
|
const result = onNewWindowHelper(baseOptions, setupWindow, {
|
||||||
baseOptions,
|
url: 'about:blank#other',
|
||||||
setupWindow,
|
});
|
||||||
'about:blank#other',
|
|
||||||
undefined,
|
|
||||||
preventDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
expect(mockCreateAboutBlank).not.toHaveBeenCalled();
|
||||||
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
expect(mockCreateNewTab).not.toHaveBeenCalled();
|
||||||
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
expect(mockShowNavigationBlockedMessage).not.toHaveBeenCalled();
|
||||||
expect(mockOpenExternal).not.toHaveBeenCalled();
|
expect(mockOpenExternal).not.toHaveBeenCalled();
|
||||||
expect(preventDefault).not.toHaveBeenCalled();
|
expect(result.action).toEqual('allow');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import {
|
|||||||
dialog,
|
dialog,
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
Event,
|
Event,
|
||||||
NewWindowWebContentsEvent,
|
|
||||||
WebContents,
|
WebContents,
|
||||||
|
HandlerDetails,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
|
|
||||||
import { linkIsInternal, nativeTabsSupported, openExternal } from './helpers';
|
import { linkIsInternal, nativeTabsSupported, openExternal } from './helpers';
|
||||||
@ -18,84 +18,64 @@ import {
|
|||||||
} from './windowHelpers';
|
} from './windowHelpers';
|
||||||
import { WindowOptions } from '../../../shared/src/options/model';
|
import { WindowOptions } from '../../../shared/src/options/model';
|
||||||
|
|
||||||
|
type NewWindowHandlerResult = ReturnType<
|
||||||
|
Parameters<WebContents['setWindowOpenHandler']>[0]
|
||||||
|
>;
|
||||||
|
|
||||||
export function onNewWindow(
|
export function onNewWindow(
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
||||||
event: NewWindowWebContentsEvent,
|
details: HandlerDetails,
|
||||||
urlToGo: string,
|
|
||||||
frameName: string,
|
|
||||||
disposition:
|
|
||||||
| 'default'
|
|
||||||
| 'foreground-tab'
|
|
||||||
| 'background-tab'
|
|
||||||
| 'new-window'
|
|
||||||
| 'save-to-disk'
|
|
||||||
| 'other',
|
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
): Promise<void> {
|
): NewWindowHandlerResult {
|
||||||
log.debug('onNewWindow', {
|
log.debug('onNewWindow', {
|
||||||
event,
|
details,
|
||||||
urlToGo,
|
|
||||||
frameName,
|
|
||||||
disposition,
|
|
||||||
parent,
|
|
||||||
});
|
});
|
||||||
const preventDefault = (newGuest?: BrowserWindow): void => {
|
|
||||||
log.debug('onNewWindow.preventDefault', { newGuest, event });
|
|
||||||
if (event.preventDefault) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
if (newGuest) {
|
|
||||||
event.newGuest = newGuest;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return onNewWindowHelper(
|
return onNewWindowHelper(
|
||||||
options,
|
options,
|
||||||
setupWindow,
|
setupWindow,
|
||||||
urlToGo,
|
details,
|
||||||
disposition,
|
nativeTabsSupported() ? undefined : parent,
|
||||||
preventDefault,
|
|
||||||
parent,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onNewWindowHelper(
|
export function onNewWindowHelper(
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
||||||
urlToGo: string,
|
details: HandlerDetails,
|
||||||
disposition: string | undefined,
|
|
||||||
preventDefault: (newGuest?: BrowserWindow) => void,
|
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
): Promise<void> {
|
): NewWindowHandlerResult {
|
||||||
log.debug('onNewWindowHelper', {
|
log.debug('onNewWindowHelper', {
|
||||||
options,
|
options,
|
||||||
urlToGo,
|
details,
|
||||||
disposition,
|
|
||||||
preventDefault,
|
|
||||||
parent,
|
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
!linkIsInternal(
|
!linkIsInternal(
|
||||||
options.targetUrl,
|
options.targetUrl,
|
||||||
urlToGo,
|
details.url,
|
||||||
options.internalUrls,
|
options.internalUrls,
|
||||||
options.strictInternalUrls,
|
options.strictInternalUrls,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
preventDefault();
|
|
||||||
if (options.blockExternalUrls) {
|
if (options.blockExternalUrls) {
|
||||||
return new Promise((resolve) => {
|
showNavigationBlockedMessage(
|
||||||
showNavigationBlockedMessage(
|
`Navigation to external URL blocked by options: ${details.url}`,
|
||||||
`Navigation to external URL blocked by options: ${urlToGo}`,
|
)
|
||||||
)
|
.then(() => {
|
||||||
.then(() => resolve())
|
// blockExternalURL(details.url).then(resolve).catch((err: unknown) => {
|
||||||
.catch((err: unknown) => {
|
// log.error('blockExternalURL', err);
|
||||||
throw err;
|
// });
|
||||||
});
|
})
|
||||||
});
|
.catch((err: unknown) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
return { action: 'deny' };
|
||||||
} else {
|
} else {
|
||||||
return openExternal(urlToGo);
|
openExternal(details.url).catch((err: unknown) => {
|
||||||
|
log.error('openExternal', err);
|
||||||
|
});
|
||||||
|
return { action: 'deny' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normally the following would be:
|
// Normally the following would be:
|
||||||
@ -103,26 +83,25 @@ export function onNewWindowHelper(
|
|||||||
// But due to a bug we resolved in https://github.com/nativefier/nativefier/issues/1197
|
// But due to a bug we resolved in https://github.com/nativefier/nativefier/issues/1197
|
||||||
// Some sites use about:blank#something to use as placeholder windows to fill
|
// Some sites use about:blank#something to use as placeholder windows to fill
|
||||||
// with content via JavaScript. So we'll stay specific for now...
|
// with content via JavaScript. So we'll stay specific for now...
|
||||||
else if (['about:blank', 'about:blank#blocked'].includes(urlToGo)) {
|
else if (['about:blank', 'about:blank#blocked'].includes(details.url)) {
|
||||||
return Promise.resolve(
|
createAboutBlankWindow(
|
||||||
preventDefault(createAboutBlankWindow(options, setupWindow, parent)),
|
options,
|
||||||
|
setupWindow,
|
||||||
|
nativeTabsSupported() ? undefined : parent,
|
||||||
);
|
);
|
||||||
|
return { action: 'deny' };
|
||||||
} else if (nativeTabsSupported()) {
|
} else if (nativeTabsSupported()) {
|
||||||
return Promise.resolve(
|
createNewTab(
|
||||||
preventDefault(
|
options,
|
||||||
createNewTab(
|
setupWindow,
|
||||||
options,
|
details.url,
|
||||||
setupWindow,
|
details.disposition === 'foreground-tab',
|
||||||
urlToGo,
|
|
||||||
disposition === 'foreground-tab',
|
|
||||||
parent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
return { action: 'deny' };
|
||||||
}
|
}
|
||||||
return Promise.resolve(undefined);
|
return { action: 'allow' };
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
return Promise.reject(err);
|
return { action: 'deny' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,14 +52,15 @@ describe('clearAppData', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('createNewTab', () => {
|
describe('createNewTab', () => {
|
||||||
const window = new BrowserWindow();
|
// const window = new BrowserWindow();
|
||||||
const options: WindowOptions = {
|
const options: WindowOptions = {
|
||||||
|
autoHideMenuBar: true,
|
||||||
blockExternalUrls: false,
|
blockExternalUrls: false,
|
||||||
insecure: false,
|
insecure: false,
|
||||||
name: 'Test App',
|
name: 'Test App',
|
||||||
targetUrl: 'https://github.com/nativefier/natifefier',
|
targetUrl: 'https://github.com/nativefier/natifefier',
|
||||||
zoom: 1.0,
|
zoom: 1.0,
|
||||||
};
|
} as WindowOptions;
|
||||||
const setupWindow = jest.fn();
|
const setupWindow = jest.fn();
|
||||||
const url = 'https://github.com/nativefier/nativefier';
|
const url = 'https://github.com/nativefier/nativefier';
|
||||||
const mockAddTabbedWindow: jest.SpyInstance = jest.spyOn(
|
const mockAddTabbedWindow: jest.SpyInstance = jest.spyOn(
|
||||||
@ -78,7 +79,7 @@ describe('createNewTab', () => {
|
|||||||
test('creates new foreground tab', () => {
|
test('creates new foreground tab', () => {
|
||||||
const foreground = true;
|
const foreground = true;
|
||||||
|
|
||||||
const tab = createNewTab(options, setupWindow, url, foreground, window);
|
const tab = createNewTab(options, setupWindow, url, foreground);
|
||||||
|
|
||||||
expect(mockAddTabbedWindow).toHaveBeenCalledWith(tab);
|
expect(mockAddTabbedWindow).toHaveBeenCalledWith(tab);
|
||||||
expect(setupWindow).toHaveBeenCalledWith(options, tab);
|
expect(setupWindow).toHaveBeenCalledWith(options, tab);
|
||||||
@ -89,7 +90,13 @@ describe('createNewTab', () => {
|
|||||||
test('creates new background tab', () => {
|
test('creates new background tab', () => {
|
||||||
const foreground = false;
|
const foreground = false;
|
||||||
|
|
||||||
const tab = createNewTab(options, setupWindow, url, foreground, window);
|
const tab = createNewTab(
|
||||||
|
options,
|
||||||
|
setupWindow,
|
||||||
|
url,
|
||||||
|
foreground,
|
||||||
|
// window
|
||||||
|
);
|
||||||
|
|
||||||
expect(mockAddTabbedWindow).toHaveBeenCalledWith(tab);
|
expect(mockAddTabbedWindow).toHaveBeenCalledWith(tab);
|
||||||
expect(setupWindow).toHaveBeenCalledWith(options, tab);
|
expect(setupWindow).toHaveBeenCalledWith(options, tab);
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
import { getCSSToInject, isOSX, nativeTabsSupported } from './helpers';
|
import { getCSSToInject, isOSX, nativeTabsSupported } from './helpers';
|
||||||
import * as log from './loggingHelper';
|
import * as log from './loggingHelper';
|
||||||
import { TrayValue, WindowOptions } from '../../../shared/src/options/model';
|
import { TrayValue, WindowOptions } from '../../../shared/src/options/model';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
const ZOOM_INTERVAL = 0.1;
|
const ZOOM_INTERVAL = 0.1;
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ export function createAboutBlankWindow(
|
|||||||
{ ...options, show: false },
|
{ ...options, show: false },
|
||||||
setupWindow,
|
setupWindow,
|
||||||
'about:blank',
|
'about:blank',
|
||||||
parent,
|
nativeTabsSupported() ? undefined : parent,
|
||||||
);
|
);
|
||||||
window.webContents.once('did-stop-loading', () => {
|
window.webContents.once('did-stop-loading', () => {
|
||||||
if (window.webContents.getURL() === 'about:blank') {
|
if (window.webContents.getURL() === 'about:blank') {
|
||||||
@ -90,11 +91,16 @@ export function createNewTab(
|
|||||||
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
setupWindow: (options: WindowOptions, window: BrowserWindow) => void,
|
||||||
url: string,
|
url: string,
|
||||||
foreground: boolean,
|
foreground: boolean,
|
||||||
parent?: BrowserWindow,
|
|
||||||
): BrowserWindow | undefined {
|
): BrowserWindow | undefined {
|
||||||
log.debug('createNewTab', { url, foreground, parent });
|
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||||
|
log.debug('createNewTab', {
|
||||||
|
url,
|
||||||
|
foreground,
|
||||||
|
focusedWindow,
|
||||||
|
});
|
||||||
return withFocusedWindow((focusedWindow) => {
|
return withFocusedWindow((focusedWindow) => {
|
||||||
const newTab = createNewWindow(options, setupWindow, url, parent);
|
const newTab = createNewWindow(options, setupWindow, url);
|
||||||
|
log.debug('createNewTab.withFocusedWindow', { focusedWindow, newTab });
|
||||||
focusedWindow.addTabbedWindow(newTab);
|
focusedWindow.addTabbedWindow(newTab);
|
||||||
if (!foreground) {
|
if (!foreground) {
|
||||||
focusedWindow.focus();
|
focusedWindow.focus();
|
||||||
@ -109,9 +115,12 @@ export function createNewWindow(
|
|||||||
url: string,
|
url: string,
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
): BrowserWindow {
|
): BrowserWindow {
|
||||||
log.debug('createNewWindow', { url, parent });
|
log.debug('createNewWindow', {
|
||||||
const window = new BrowserWindow({
|
url,
|
||||||
parent,
|
parent,
|
||||||
|
});
|
||||||
|
const window = new BrowserWindow({
|
||||||
|
parent: nativeTabsSupported() ? undefined : parent,
|
||||||
...getDefaultWindowOptions(options),
|
...getDefaultWindowOptions(options),
|
||||||
});
|
});
|
||||||
setupWindow(options, window);
|
setupWindow(options, window);
|
||||||
@ -141,8 +150,11 @@ export function getDefaultWindowOptions(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const defaultOptions: BrowserWindowConstructorOptions = {
|
const defaultOptions: BrowserWindowConstructorOptions = {
|
||||||
|
autoHideMenuBar: options.autoHideMenuBar,
|
||||||
fullscreenable: true,
|
fullscreenable: true,
|
||||||
tabbingIdentifier: nativeTabsSupported() ? options.name : undefined,
|
tabbingIdentifier: nativeTabsSupported()
|
||||||
|
? options.tabbingIdentifier ?? randomUUID()
|
||||||
|
: undefined,
|
||||||
title: options.name,
|
title: options.name,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
javascript: true,
|
javascript: true,
|
||||||
|
@ -202,7 +202,8 @@ const setDockBadge = isOSX()
|
|||||||
? (count?: number | string, bounce = false): void => {
|
? (count?: number | string, bounce = false): void => {
|
||||||
if (count !== undefined) {
|
if (count !== undefined) {
|
||||||
app.dock.setBadge(count.toString());
|
app.dock.setBadge(count.toString());
|
||||||
if (bounce && count > currentBadgeCount) app.dock.bounce();
|
if (bounce && typeof count === 'number' && count > currentBadgeCount)
|
||||||
|
app.dock.bounce();
|
||||||
currentBadgeCount = typeof count === 'number' ? count : 0;
|
currentBadgeCount = typeof count === 'number' ? count : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,10 +310,10 @@ if (shouldQuit) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('new-window-for-tab', () => {
|
app.on('new-window-for-tab', (event: Event) => {
|
||||||
log.debug('app.new-window-for-tab');
|
log.debug('app.new-window-for-tab', { event });
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.emit('new-tab');
|
mainWindow.emit('new-window-for-tab', event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -332,9 +333,10 @@ app.on(
|
|||||||
if (appArgs.basicAuthUsername && appArgs.basicAuthPassword) {
|
if (appArgs.basicAuthUsername && appArgs.basicAuthPassword) {
|
||||||
callback(appArgs.basicAuthUsername, appArgs.basicAuthPassword);
|
callback(appArgs.basicAuthUsername, appArgs.basicAuthPassword);
|
||||||
} else {
|
} else {
|
||||||
createLoginWindow(callback, mainWindow).catch((err) =>
|
createLoginWindow(
|
||||||
log.error('createLoginWindow ERROR', err),
|
callback,
|
||||||
);
|
// mainWindow
|
||||||
|
).catch((err) => log.error('createLoginWindow ERROR', err));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,6 @@ module.exports = {
|
|||||||
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
|
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
|
||||||
'@typescript-eslint/no-explicit-any': 'error',
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
'@typescript-eslint/no-extraneous-class': 'error',
|
'@typescript-eslint/no-extraneous-class': 'error',
|
||||||
'@typescript-eslint/no-implicit-any-catch': 'error',
|
|
||||||
'@typescript-eslint/no-invalid-void-type': 'error',
|
'@typescript-eslint/no-invalid-void-type': 'error',
|
||||||
'@typescript-eslint/prefer-ts-expect-error': 'error',
|
'@typescript-eslint/prefer-ts-expect-error': 'error',
|
||||||
'@typescript-eslint/type-annotation-spacing': 'error',
|
'@typescript-eslint/type-annotation-spacing': 'error',
|
||||||
|
8531
npm-shrinkwrap.json
generated
8531
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@ -58,43 +58,44 @@
|
|||||||
"watch": "npx concurrently \"npm:*:watch\""
|
"watch": "npx concurrently \"npm:*:watch\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.1.3",
|
"@electron/asar": "^3.2.4",
|
||||||
"electron-packager": "^15.5.1",
|
"axios": "^1.4.0",
|
||||||
"fs-extra": "^10.0.0",
|
"electron-packager": "^17.1.1",
|
||||||
"gitcloud": "^0.2.3",
|
"fs-extra": "^11.1.1",
|
||||||
|
"gitcloud": "^0.2.4",
|
||||||
"hasbin": "^1.2.3",
|
"hasbin": "^1.2.3",
|
||||||
"loglevel": "^1.7.1",
|
"loglevel": "^1.8.1",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"page-icon": "^0.4.0",
|
"page-icon": "^0.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.21",
|
||||||
"tmp": "^0.2.1",
|
"tmp": "^0.2.1",
|
||||||
"yargs": "^17.1.1"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/debug": "^4.1.6",
|
"@types/debug": "^4.1.8",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^11.0.1",
|
||||||
"@types/hasbin": "^1.2.0",
|
"@types/hasbin": "^1.2.0",
|
||||||
"@types/jest": "^28.1.6",
|
"@types/jest": "^29.5.3",
|
||||||
"@types/ncp": "^2.0.5",
|
"@types/ncp": "^2.0.5",
|
||||||
"@types/node": "^16.0.0",
|
"@types/node": "^20.4.7",
|
||||||
"@types/page-icon": "^0.3.4",
|
"@types/page-icon": "^0.3.4",
|
||||||
"@types/tmp": "^0.2.1",
|
"@types/tmp": "^0.2.3",
|
||||||
"@types/yargs": "^17.0.10",
|
"@types/yargs": "^17.0.24",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||||
"@typescript-eslint/parser": "^5.3.0",
|
"@typescript-eslint/parser": "^6.2.1",
|
||||||
"electron": "^21.4.4",
|
"electron": "^25.5.0",
|
||||||
"eslint": "^8.1.0",
|
"eslint": "^8.46.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.10.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"jest": "^28.1.3",
|
"jest": "^29.6.2",
|
||||||
"playwright": "^1.24.0",
|
"playwright": "^1.36.2",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^3.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^5.0.1",
|
||||||
"ts-loader": "^9.2.3",
|
"ts-loader": "^9.4.4",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^5.1.6",
|
||||||
"webpack": "^5.45.1",
|
"webpack": "^5.88.2",
|
||||||
"webpack-cli": "^4.7.2"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
"jest_COMMENTS": {
|
"jest_COMMENTS": {
|
||||||
"testPathIgnorePatterns": "See https://jestjs.io/docs/configuration#testpathignorepatterns-arraystring . We set it to 1. ignore coverage for deps, and 2. be sure we test the compiled JS, which is in `lib`, not `src` or `dist`",
|
"testPathIgnorePatterns": "See https://jestjs.io/docs/configuration#testpathignorepatterns-arraystring . We set it to 1. ignore coverage for deps, and 2. be sure we test the compiled JS, which is in `lib`, not `src` or `dist`",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CreateOptions } from 'asar';
|
import { CreateOptions } from '@electron/asar';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
import * as electronPackager from 'electron-packager';
|
import * as electronPackager from 'electron-packager';
|
||||||
|
|
||||||
export type TitleBarValue =
|
export type TitleBarValue =
|
||||||
@ -117,6 +118,7 @@ export type OutputOptions = NativefierOptions & {
|
|||||||
nativefierVersion: string;
|
nativefierVersion: string;
|
||||||
oldBuildWarningText: string;
|
oldBuildWarningText: string;
|
||||||
strictInternalUrls: boolean;
|
strictInternalUrls: boolean;
|
||||||
|
tabbingIdentifier?: string;
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
zoom?: number;
|
zoom?: number;
|
||||||
@ -205,6 +207,7 @@ export type RawOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WindowOptions = {
|
export type WindowOptions = {
|
||||||
|
autoHideMenuBar: boolean;
|
||||||
blockExternalUrls: boolean;
|
blockExternalUrls: boolean;
|
||||||
browserwindowOptions?: BrowserWindowOptions;
|
browserwindowOptions?: BrowserWindowOptions;
|
||||||
insecure: boolean;
|
insecure: boolean;
|
||||||
@ -213,6 +216,7 @@ export type WindowOptions = {
|
|||||||
name: string;
|
name: string;
|
||||||
proxyRules?: string;
|
proxyRules?: string;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
|
tabbingIdentifier?: string;
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
@ -220,10 +224,15 @@ export type WindowOptions = {
|
|||||||
|
|
||||||
export function outputOptionsToWindowOptions(
|
export function outputOptionsToWindowOptions(
|
||||||
options: OutputOptions,
|
options: OutputOptions,
|
||||||
|
generateTabbingIdentifierIfMissing: boolean,
|
||||||
): WindowOptions {
|
): WindowOptions {
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
|
autoHideMenuBar: !options.showMenuBar,
|
||||||
insecure: options.insecure ?? false,
|
insecure: options.insecure ?? false,
|
||||||
|
tabbingIdentifier: generateTabbingIdentifierIfMissing
|
||||||
|
? options.tabbingIdentifier ?? randomUUID()
|
||||||
|
: options.tabbingIdentifier,
|
||||||
zoom: options.zoom ?? 1.0,
|
zoom: options.zoom ?? 1.0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,19 @@ export const DEFAULT_APP_NAME = 'APP';
|
|||||||
// - upgrade app / package.json / "devDependencies" / "electron"
|
// - upgrade app / package.json / "devDependencies" / "electron"
|
||||||
// - upgrade package.json / "devDependencies" / "electron"
|
// - upgrade package.json / "devDependencies" / "electron"
|
||||||
// Doing a *major* upgrade? Read https://github.com/nativefier/nativefier/blob/master/HACKING.md#deps-major-upgrading-electron
|
// Doing a *major* upgrade? Read https://github.com/nativefier/nativefier/blob/master/HACKING.md#deps-major-upgrading-electron
|
||||||
export const DEFAULT_ELECTRON_VERSION = '21.4.4';
|
export const DEFAULT_ELECTRON_VERSION = '25.5.0';
|
||||||
// https://atom.io/download/atom-shell/index.json
|
// https://atom.io/download/atom-shell/index.json
|
||||||
// https://www.electronjs.org/releases/stable
|
// https://www.electronjs.org/releases/stable
|
||||||
export const DEFAULT_CHROME_VERSION = '106.0.5249.199';
|
export const DEFAULT_CHROME_VERSION = '114.0.5735.289';
|
||||||
|
|
||||||
// Update each of these periodically
|
// Update each of these periodically
|
||||||
// https://product-details.mozilla.org/1.0/firefox_versions.json
|
// https://product-details.mozilla.org/1.0/firefox_versions.json
|
||||||
export const DEFAULT_FIREFOX_VERSION = '116.0';
|
export const DEFAULT_FIREFOX_VERSION = '116.0.1';
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Safari_version_history
|
// https://en.wikipedia.org/wiki/Safari_version_history
|
||||||
export const DEFAULT_SAFARI_VERSION = {
|
export const DEFAULT_SAFARI_VERSION = {
|
||||||
majorVersion: 65,
|
majorVersion: 16,
|
||||||
version: '16.5.2',
|
version: '16.6',
|
||||||
webkitVersion: '605.1.15',
|
webkitVersion: '605.1.15',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"incremental": true,
|
"incremental": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"noImplicitAny": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
Loading…
Reference in New Issue
Block a user