mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2025-01-22 22:58:33 +00:00
Improve user agent handling/provide user agent "short" codes (#1198)
This commit is contained in:
parent
4f3b449218
commit
d6730f7022
75
API.md
75
API.md
@ -52,10 +52,13 @@
|
|||||||
- [[zoom]](#zoom)
|
- [[zoom]](#zoom)
|
||||||
- [Internal Browser Options](#internal-browser-options)
|
- [Internal Browser Options](#internal-browser-options)
|
||||||
- [[file-download-options]](#file-download-options)
|
- [[file-download-options]](#file-download-options)
|
||||||
- [[honest]](#honest)
|
|
||||||
- [[inject]](#inject)
|
- [[inject]](#inject)
|
||||||
- [[lang]](#lang)
|
- [[lang]](#lang)
|
||||||
- [[user-agent]](#user-agent)
|
- [[user-agent]](#user-agent)
|
||||||
|
- [[user-agent-honest]](#user-agent-honest)
|
||||||
|
- [Internal Browser Cache Options](#internal-browser-cache-options)
|
||||||
|
- [[clear-cache]](#clear-cache)
|
||||||
|
- [[disk-cache-size]](#disk-cache-size)
|
||||||
- [URL Handling Options](#url-handling-options)
|
- [URL Handling Options](#url-handling-options)
|
||||||
- [[block-external-urls]](#block-external-urls)
|
- [[block-external-urls]](#block-external-urls)
|
||||||
- [[internal-urls]](#internal-urls)
|
- [[internal-urls]](#internal-urls)
|
||||||
@ -66,9 +69,6 @@
|
|||||||
- [[disable-gpu]](#disable-gpu)
|
- [[disable-gpu]](#disable-gpu)
|
||||||
- [[enable-es3-apis]](#enable-es3-apis)
|
- [[enable-es3-apis]](#enable-es3-apis)
|
||||||
- [[ignore-gpu-blacklist]](#ignore-gpu-blacklist)
|
- [[ignore-gpu-blacklist]](#ignore-gpu-blacklist)
|
||||||
- [Caching Options](#caching-options)
|
|
||||||
- [[clear-cache]](#clear-cache)
|
|
||||||
- [[disk-cache-size]](#disk-cache-size)
|
|
||||||
- [(In)Security Options](#in-security-options)
|
- [(In)Security Options](#in-security-options)
|
||||||
- [[disable-old-build-warning-yesiknowitisinsecure]](#disable-old-build-warning-yesiknowitisinsecure)
|
- [[disable-old-build-warning-yesiknowitisinsecure]](#disable-old-build-warning-yesiknowitisinsecure)
|
||||||
- [[ignore-certificate]](#ignore-certificate)
|
- [[ignore-certificate]](#ignore-certificate)
|
||||||
@ -672,16 +672,6 @@ Example `shortcuts.json` for `https://deezer.com` & `https://soundcloud.com` to
|
|||||||
|
|
||||||
On MacOS 10.14+, if you have set a global shortcut that includes a Media key, the user will need to be prompted for permissions to enable these keys in System Preferences > Security & Privacy > Accessibility.
|
On MacOS 10.14+, if you have set a global shortcut that includes a Media key, the user will need to be prompted for permissions to enable these keys in System Preferences > Security & Privacy > Accessibility.
|
||||||
|
|
||||||
#### [honest]
|
|
||||||
|
|
||||||
```
|
|
||||||
--honest
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, Nativefier uses a preset user agent string for your OS and masquerades as a regular Google Chrome browser, so that sites like WhatsApp Web will not say that the current browser is unsupported.
|
|
||||||
|
|
||||||
If this flag is passed, it will not override the user agent.
|
|
||||||
|
|
||||||
#### [inject]
|
#### [inject]
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -712,7 +702,41 @@ Set the language or locale to render the web site as (e.g., "fr", "en-US", "es",
|
|||||||
-u, --user-agent <value>
|
-u, --user-agent <value>
|
||||||
```
|
```
|
||||||
|
|
||||||
Set the user agent to run the created app with.
|
Set the user agent to run the created app with. Use `--user-agent-honest` to use the true Electron user agent.
|
||||||
|
|
||||||
|
The following short codes are also supported to generate a user agent: `edge`, `firefox`, `safari`.
|
||||||
|
|
||||||
|
- `edge` will generate a Microsoft Edge user agent matching the Chrome version of Electron being used
|
||||||
|
- `firefox` will generate a Mozilla Firefox user agent matching the latest stable release of that browser
|
||||||
|
- `safari` will generate an Apple Safari user agent matching the latest stable release of that browser
|
||||||
|
|
||||||
|
#### [user-agent-honest]
|
||||||
|
|
||||||
|
```
|
||||||
|
--user-agent-honest, --honest
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nativefier uses a preset user agent string for your OS and masquerades as a regular Google Chrome browser, so that for some sites, it will not say that the current browser is unsupported.
|
||||||
|
|
||||||
|
If this flag is passed, it will not override the user agent, and use Electron's default generated one for your app.
|
||||||
|
|
||||||
|
### Internal Browser Cache Options
|
||||||
|
|
||||||
|
#### [clear-cache]
|
||||||
|
|
||||||
|
```
|
||||||
|
--clear-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
Prevents the application from preserving cache between launches.
|
||||||
|
|
||||||
|
#### [disk-cache-size]
|
||||||
|
|
||||||
|
```
|
||||||
|
--disk-cache-size <value>
|
||||||
|
```
|
||||||
|
|
||||||
|
Forces the maximum disk space to be used by the disk cache. Value is given in bytes.
|
||||||
|
|
||||||
### URL Handling Options
|
### URL Handling Options
|
||||||
|
|
||||||
@ -818,27 +842,6 @@ Passes the enable-es3-apis flag to the Chrome engine, to force the activation of
|
|||||||
|
|
||||||
Passes the ignore-gpu-blacklist flag to the Chrome engine, to allow for WebGl apps to work on non supported graphics cards.
|
Passes the ignore-gpu-blacklist flag to the Chrome engine, to allow for WebGl apps to work on non supported graphics cards.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Caching Options
|
|
||||||
|
|
||||||
#### [clear-cache]
|
|
||||||
|
|
||||||
```
|
|
||||||
--clear-cache
|
|
||||||
```
|
|
||||||
|
|
||||||
Prevents the application from preserving cache between launches.
|
|
||||||
|
|
||||||
#### [disk-cache-size]
|
|
||||||
|
|
||||||
```
|
|
||||||
--disk-cache-size <value>
|
|
||||||
```
|
|
||||||
|
|
||||||
Forces the maximum disk space to be used by the disk cache. Value is given in bytes.
|
|
||||||
|
|
||||||
### (In)Security Options
|
### (In)Security Options
|
||||||
|
|
||||||
#### [ignore-certificate]
|
#### [ignore-certificate]
|
||||||
|
@ -15,7 +15,7 @@ Below you'll find a list of build commands contributed by the Nativefier communi
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
nativefier 'https://docs.google.com/spreadsheets' \
|
nativefier 'https://docs.google.com/spreadsheets' \
|
||||||
--user-agent 'user agent of current stable Firefox'
|
--user-agent firefox
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: lying about the User Agent is required, else Google will notice your "Chrome" isn't a real Chrome, and will refuse access.
|
Note: lying about the User Agent is required, else Google will notice your "Chrome" isn't a real Chrome, and will refuse access.
|
||||||
@ -58,7 +58,7 @@ Note: as for Udemy, `--widevine` + [app signing](https://github.com/nativefier/n
|
|||||||
```sh
|
```sh
|
||||||
nativefier 'https://open.spotify.com/'
|
nativefier 'https://open.spotify.com/'
|
||||||
--widevine
|
--widevine
|
||||||
-u 'useragent of a non-Chrome browser, e.g. the current stable Firefox'
|
--user-agent firefox
|
||||||
--inject spotify.js
|
--inject spotify.js
|
||||||
--inject spotify.css
|
--inject spotify.css
|
||||||
```
|
```
|
||||||
|
@ -20,6 +20,6 @@
|
|||||||
"source-map-support": "^0.5.19"
|
"source-map-support": "^0.5.19"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^12.0.1"
|
"electron": "^12.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { linkIsInternal, getCounterValue } from './helpers';
|
import {
|
||||||
|
linkIsInternal,
|
||||||
|
getCounterValue,
|
||||||
|
removeUserAgentSpecifics,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
const internalUrl = 'https://medium.com/';
|
const internalUrl = 'https://medium.com/';
|
||||||
const internalUrlWww = 'https://www.medium.com/';
|
const internalUrlWww = 'https://www.medium.com/';
|
||||||
@ -146,3 +150,19 @@ test('getCounterValue should return a string for small counter numbers in the ti
|
|||||||
test('getCounterValue should return a string for large counter numbers in the title', () => {
|
test('getCounterValue should return a string for large counter numbers in the title', () => {
|
||||||
expect(getCounterValue(largeCounterTitle)).toEqual('8,756');
|
expect(getCounterValue(largeCounterTitle)).toEqual('8,756');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('removeUserAgentSpecifics', () => {
|
||||||
|
test('removes Electron and App specific info', () => {
|
||||||
|
const userAgentFallback =
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) app-nativefier-804458/1.0.0 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36';
|
||||||
|
expect(
|
||||||
|
removeUserAgentSpecifics(
|
||||||
|
userAgentFallback,
|
||||||
|
'app-nativefier-804458',
|
||||||
|
'1.0.0',
|
||||||
|
),
|
||||||
|
).not.toBe(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -160,3 +160,18 @@ export function getCounterValue(title: string): string {
|
|||||||
const match = itemCountRegex.exec(title);
|
const match = itemCountRegex.exec(title);
|
||||||
return match ? match[1] : undefined;
|
return match ? match[1] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeUserAgentSpecifics(
|
||||||
|
userAgentFallback: string,
|
||||||
|
appName: string,
|
||||||
|
appVersion: string,
|
||||||
|
): string {
|
||||||
|
// Electron userAgentFallback is the user agent used if none is specified when creating a window.
|
||||||
|
// For our purposes, it's useful because its format is similar enough to a real Chrome's user agent to not need
|
||||||
|
// to infer the userAgent. userAgentFallback normally looks like this:
|
||||||
|
// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) app-nativefier-804458/1.0.0 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36
|
||||||
|
// We just need to strip out the appName/1.0.0 and Electron/electronVersion
|
||||||
|
return userAgentFallback
|
||||||
|
.replace(`Electron/${process.versions.electron} `, '')
|
||||||
|
.replace(`${appName}/${appVersion} `, ' ');
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
APP_ARGS_FILE_PATH,
|
APP_ARGS_FILE_PATH,
|
||||||
} from './components/mainWindow';
|
} from './components/mainWindow';
|
||||||
import { createTrayIcon } from './components/trayIcon';
|
import { createTrayIcon } from './components/trayIcon';
|
||||||
import { isOSX } from './helpers/helpers';
|
import { isOSX, removeUserAgentSpecifics } from './helpers/helpers';
|
||||||
import { inferFlashPath } from './helpers/inferFlash';
|
import { inferFlashPath } from './helpers/inferFlash';
|
||||||
|
|
||||||
// Entrypoint for Squirrel, a windows update framework. See https://github.com/nativefier/nativefier/pull/744
|
// Entrypoint for Squirrel, a windows update framework. See https://github.com/nativefier/nativefier/pull/744
|
||||||
@ -46,6 +46,14 @@ if (appArgs.portable) {
|
|||||||
app.setPath('userData', path.join(__dirname, '..', 'appData'));
|
app.setPath('userData', path.join(__dirname, '..', 'appData'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!appArgs.userAgentHonest) {
|
||||||
|
app.userAgentFallback = removeUserAgentSpecifics(
|
||||||
|
app.userAgentFallback,
|
||||||
|
app.getName(),
|
||||||
|
app.getVersion(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Take in a URL on the command line as an override
|
// Take in a URL on the command line as an override
|
||||||
if (process.argv.length > 1) {
|
if (process.argv.length > 1) {
|
||||||
const maybeUrl = process.argv[1];
|
const maybeUrl = process.argv[1];
|
||||||
|
@ -53,7 +53,6 @@ function pickElectronAppArgs(options: AppOptions): any {
|
|||||||
height: options.nativefier.height,
|
height: options.nativefier.height,
|
||||||
helperBundleId: options.packager.helperBundleId,
|
helperBundleId: options.packager.helperBundleId,
|
||||||
hideWindowFrame: options.nativefier.hideWindowFrame,
|
hideWindowFrame: options.nativefier.hideWindowFrame,
|
||||||
honest: options.nativefier.honest,
|
|
||||||
ignoreCertificate: options.nativefier.ignoreCertificate,
|
ignoreCertificate: options.nativefier.ignoreCertificate,
|
||||||
ignoreGpuBlacklist: options.nativefier.ignoreGpuBlacklist,
|
ignoreGpuBlacklist: options.nativefier.ignoreGpuBlacklist,
|
||||||
insecure: options.nativefier.insecure,
|
insecure: options.nativefier.insecure,
|
||||||
@ -83,7 +82,7 @@ function pickElectronAppArgs(options: AppOptions): any {
|
|||||||
tray: options.nativefier.tray,
|
tray: options.nativefier.tray,
|
||||||
usageDescription: options.packager.usageDescription,
|
usageDescription: options.packager.usageDescription,
|
||||||
userAgent: options.nativefier.userAgent,
|
userAgent: options.nativefier.userAgent,
|
||||||
userAgentOverriden: options.nativefier.userAgentOverriden,
|
userAgentHonest: options.nativefier.userAgentHonest,
|
||||||
versionString: options.nativefier.versionString,
|
versionString: options.nativefier.versionString,
|
||||||
width: options.nativefier.width,
|
width: options.nativefier.width,
|
||||||
widevine: options.nativefier.widevine,
|
widevine: options.nativefier.widevine,
|
||||||
|
@ -300,7 +300,8 @@ export function initArgs(argv: string[]): yargs.Argv<any> {
|
|||||||
})
|
})
|
||||||
.option('u', {
|
.option('u', {
|
||||||
alias: 'user-agent',
|
alias: 'user-agent',
|
||||||
description: "set the app's user agent string",
|
description:
|
||||||
|
"set the app's user agent string; may also use 'edge', 'firefox', or 'safari' to have one auto-generated",
|
||||||
type: 'string',
|
type: 'string',
|
||||||
})
|
})
|
||||||
.option('user-agent-honest', {
|
.option('user-agent-honest', {
|
||||||
|
@ -2,10 +2,22 @@ import * as path from 'path';
|
|||||||
|
|
||||||
export const DEFAULT_APP_NAME = 'APP';
|
export const DEFAULT_APP_NAME = 'APP';
|
||||||
|
|
||||||
// Update both together, and update app / package.json / devDeps / electron
|
// Update both DEFAULT_ELECTRON_VERSION and DEFAULT_CHROME_VERSION together,
|
||||||
|
// and update app / package.json / devDeps / electron to value of DEFAULT_ELECTRON_VERSION
|
||||||
export const DEFAULT_ELECTRON_VERSION = '12.0.7';
|
export const DEFAULT_ELECTRON_VERSION = '12.0.7';
|
||||||
export const DEFAULT_CHROME_VERSION = '89.0.4389.128';
|
export const DEFAULT_CHROME_VERSION = '89.0.4389.128';
|
||||||
|
|
||||||
|
// Update each of these periodically
|
||||||
|
// https://product-details.mozilla.org/1.0/firefox_versions.json
|
||||||
|
export const DEFAULT_FIREFOX_VERSION = '88.0.1';
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Safari_version_history
|
||||||
|
export const DEFAULT_SAFARI_VERSION = {
|
||||||
|
majorVersion: 14,
|
||||||
|
version: '14.0.3',
|
||||||
|
webkitVersion: '610.4.3.1.7',
|
||||||
|
};
|
||||||
|
|
||||||
export const ELECTRON_MAJOR_VERSION = parseInt(
|
export const ELECTRON_MAJOR_VERSION = parseInt(
|
||||||
DEFAULT_ELECTRON_VERSION.split('.')[0],
|
DEFAULT_ELECTRON_VERSION.split('.')[0],
|
||||||
10,
|
10,
|
||||||
|
@ -184,13 +184,6 @@ export function useOldAppOptions(
|
|||||||
log.debug('rawOptions', rawOptions);
|
log.debug('rawOptions', rawOptions);
|
||||||
log.debug('oldApp', oldApp);
|
log.debug('oldApp', oldApp);
|
||||||
|
|
||||||
if (
|
|
||||||
oldApp.options.userAgentOverriden === undefined ||
|
|
||||||
oldApp.options.userAgentOverriden === false
|
|
||||||
) {
|
|
||||||
oldApp.options.userAgent = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedOptions = { ...rawOptions, ...oldApp.options };
|
const combinedOptions = { ...rawOptions, ...oldApp.options };
|
||||||
|
|
||||||
log.debug('Combined options', combinedOptions);
|
log.debug('Combined options', combinedOptions);
|
||||||
|
58
src/infer/browsers/inferChromeVersion.ts
Normal file
58
src/infer/browsers/inferChromeVersion.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import * as log from 'loglevel';
|
||||||
|
import {
|
||||||
|
DEFAULT_CHROME_VERSION,
|
||||||
|
DEFAULT_ELECTRON_VERSION,
|
||||||
|
} from '../../constants';
|
||||||
|
|
||||||
|
type ElectronRelease = {
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
node: string;
|
||||||
|
v8: string;
|
||||||
|
uv: string;
|
||||||
|
zlib: string;
|
||||||
|
openssl: string;
|
||||||
|
modules: string;
|
||||||
|
chrome: string;
|
||||||
|
files: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ELECTRON_VERSIONS_URL = 'https://atom.io/download/atom-shell/index.json';
|
||||||
|
|
||||||
|
export async function getChromeVersionForElectronVersion(
|
||||||
|
electronVersion: string,
|
||||||
|
url = ELECTRON_VERSIONS_URL,
|
||||||
|
): Promise<string> {
|
||||||
|
if (!electronVersion || electronVersion === DEFAULT_ELECTRON_VERSION) {
|
||||||
|
// Exit quickly for the scenario that we already know about
|
||||||
|
return DEFAULT_CHROME_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug('Grabbing electron<->chrome versions file from', url);
|
||||||
|
const response = await axios.get(url, { timeout: 5000 });
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(`Bad request: Status code ${response.status}`);
|
||||||
|
}
|
||||||
|
const electronReleases: ElectronRelease[] = response.data;
|
||||||
|
const electronVersionToChromeVersion: { [key: string]: string } = {};
|
||||||
|
for (const release of electronReleases) {
|
||||||
|
electronVersionToChromeVersion[release.version] = release.chrome;
|
||||||
|
}
|
||||||
|
if (!(electronVersion in electronVersionToChromeVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`Electron version '${electronVersion}' not found in retrieved version list!`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const chromeVersion = electronVersionToChromeVersion[electronVersion];
|
||||||
|
log.debug(
|
||||||
|
`Associated electron v${electronVersion} to chrome v${chromeVersion}`,
|
||||||
|
);
|
||||||
|
return chromeVersion;
|
||||||
|
} catch (err) {
|
||||||
|
log.error('getChromeVersionForElectronVersion ERROR', err);
|
||||||
|
log.debug('Falling back to default Chrome version', DEFAULT_CHROME_VERSION);
|
||||||
|
return DEFAULT_CHROME_VERSION;
|
||||||
|
}
|
||||||
|
}
|
49
src/infer/browsers/inferFirefoxVersion.ts
Normal file
49
src/infer/browsers/inferFirefoxVersion.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import * as log from 'loglevel';
|
||||||
|
import { DEFAULT_FIREFOX_VERSION } from '../../constants';
|
||||||
|
|
||||||
|
type FirefoxVersions = {
|
||||||
|
FIREFOX_AURORA: string;
|
||||||
|
FIREFOX_DEVEDITION: string;
|
||||||
|
FIREFOX_ESR: string;
|
||||||
|
FIREFOX_ESR_NEXT: string;
|
||||||
|
FIREFOX_NIGHTLY: string;
|
||||||
|
LAST_MERGE_DATE: string;
|
||||||
|
LAST_RELEASE_DATE: string;
|
||||||
|
LAST_SOFTFREEZE_DATE: string;
|
||||||
|
LATEST_FIREFOX_DEVEL_VERSION: string;
|
||||||
|
LATEST_FIREFOX_OLDER_VERSION: string;
|
||||||
|
LATEST_FIREFOX_RELEASED_DEVEL_VERSION: string;
|
||||||
|
LATEST_FIREFOX_VERSION: string;
|
||||||
|
NEXT_MERGE_DATE: string;
|
||||||
|
NEXT_RELEASE_DATE: string;
|
||||||
|
NEXT_SOFTFREEZE_DATE: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FIREFOX_VERSIONS_URL =
|
||||||
|
'https://product-details.mozilla.org/1.0/firefox_versions.json';
|
||||||
|
|
||||||
|
export async function getLatestFirefoxVersion(
|
||||||
|
url = FIREFOX_VERSIONS_URL,
|
||||||
|
): Promise<string> {
|
||||||
|
try {
|
||||||
|
log.debug('Grabbing Firefox version data from', url);
|
||||||
|
const response = await axios.get(url, { timeout: 5000 });
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(`Bad request: Status code ${response.status}`);
|
||||||
|
}
|
||||||
|
const firefoxVersions: FirefoxVersions = response.data;
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
`Got latest Firefox version ${firefoxVersions.LATEST_FIREFOX_VERSION}`,
|
||||||
|
);
|
||||||
|
return firefoxVersions.LATEST_FIREFOX_VERSION;
|
||||||
|
} catch (err) {
|
||||||
|
log.error('getLatestFirefoxVersion ERROR', err);
|
||||||
|
log.debug(
|
||||||
|
'Falling back to default Firefox version',
|
||||||
|
DEFAULT_FIREFOX_VERSION,
|
||||||
|
);
|
||||||
|
return DEFAULT_FIREFOX_VERSION;
|
||||||
|
}
|
||||||
|
}
|
74
src/infer/browsers/inferSafariVersion.ts
Normal file
74
src/infer/browsers/inferSafariVersion.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import * as log from 'loglevel';
|
||||||
|
import { DEFAULT_SAFARI_VERSION } from '../../constants';
|
||||||
|
|
||||||
|
export type SafariVersion = {
|
||||||
|
majorVersion: number;
|
||||||
|
version: string;
|
||||||
|
webkitVersion: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SAFARI_VERSIONS_HISTORY_URL =
|
||||||
|
'https://en.wikipedia.org/wiki/Safari_version_history';
|
||||||
|
|
||||||
|
export async function getLatestSafariVersion(
|
||||||
|
url = SAFARI_VERSIONS_HISTORY_URL,
|
||||||
|
): Promise<SafariVersion> {
|
||||||
|
try {
|
||||||
|
log.debug('Grabbing apple version data from', url);
|
||||||
|
const response = await axios.get(url, { timeout: 5000 });
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(`Bad request: Status code ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would be easier with an HTML parser, but we're not going to include an extra dependency for something that dumb
|
||||||
|
const rawData: string = response.data;
|
||||||
|
|
||||||
|
const majorVersions = [
|
||||||
|
...rawData.matchAll(
|
||||||
|
/class="mw-headline" id="Safari_[0-9]*">Safari ([0-9]*)</g,
|
||||||
|
),
|
||||||
|
].map((match) => match[1]);
|
||||||
|
|
||||||
|
const majorVersion = parseInt(majorVersions[majorVersions.length - 1]);
|
||||||
|
|
||||||
|
const majorVersionTable = rawData
|
||||||
|
.split('>Release history<')[2]
|
||||||
|
.split('<table')
|
||||||
|
.filter((table) => table.includes(`Safari ${majorVersion}.x`))[0];
|
||||||
|
|
||||||
|
const versionRows = majorVersionTable.split('<tbody')[1].split('<tr');
|
||||||
|
|
||||||
|
let version = null;
|
||||||
|
let webkitVersion: string = null;
|
||||||
|
|
||||||
|
for (const versionRow of versionRows.reverse()) {
|
||||||
|
const versionMatch = [
|
||||||
|
...versionRow.matchAll(/>\s*(([0-9]*\.){2}[0-9])\s*</g),
|
||||||
|
];
|
||||||
|
if (versionMatch.length > 0 && !version) {
|
||||||
|
version = versionMatch[0][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const webkitVersionMatch = [
|
||||||
|
...versionRow.matchAll(/>\s*(([0-9]*\.){3,4}[0-9])\s*</g),
|
||||||
|
];
|
||||||
|
if (webkitVersionMatch.length > 0 && !webkitVersion) {
|
||||||
|
webkitVersion = webkitVersionMatch[0][1];
|
||||||
|
}
|
||||||
|
if (version && webkitVersion) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
majorVersion,
|
||||||
|
version,
|
||||||
|
webkitVersion,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
log.error('getLatestSafariVersion ERROR', err);
|
||||||
|
log.debug('Falling back to default Safari version', DEFAULT_SAFARI_VERSION);
|
||||||
|
return DEFAULT_SAFARI_VERSION;
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import { inferUserAgent } from './inferUserAgent';
|
|
||||||
import { DEFAULT_ELECTRON_VERSION, DEFAULT_CHROME_VERSION } from '../constants';
|
|
||||||
|
|
||||||
const EXPECTED_USERAGENTS = {
|
|
||||||
darwin: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${DEFAULT_CHROME_VERSION} Safari/537.36`,
|
|
||||||
mas: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${DEFAULT_CHROME_VERSION} Safari/537.36`,
|
|
||||||
win32: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${DEFAULT_CHROME_VERSION} Safari/537.36`,
|
|
||||||
linux: `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${DEFAULT_CHROME_VERSION} Safari/537.36`,
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Infer User Agent', () => {
|
|
||||||
// TODO make fast by mocking timeout.
|
|
||||||
for (const [platform, archUa] of Object.entries(EXPECTED_USERAGENTS)) {
|
|
||||||
test(`Can infer userAgent for ${platform}`, async () => {
|
|
||||||
jest.setTimeout(10000);
|
|
||||||
const ua = await inferUserAgent(DEFAULT_ELECTRON_VERSION, platform);
|
|
||||||
expect(ua).toBe(archUa);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO make fast by mocking timeout, and un-skip
|
|
||||||
test.skip('Connection error will still get a user agent', async () => {
|
|
||||||
jest.setTimeout(6000);
|
|
||||||
|
|
||||||
const TIMEOUT_URL = 'http://www.google.com:81/';
|
|
||||||
await expect(inferUserAgent('1.6.7', 'darwin', TIMEOUT_URL)).resolves.toBe(
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,94 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import * as log from 'loglevel';
|
|
||||||
import { DEFAULT_CHROME_VERSION } from '../constants';
|
|
||||||
|
|
||||||
const ELECTRON_VERSIONS_URL = 'https://atom.io/download/atom-shell/index.json';
|
|
||||||
|
|
||||||
type ElectronRelease = {
|
|
||||||
version: string;
|
|
||||||
date: string;
|
|
||||||
node: string;
|
|
||||||
v8: string;
|
|
||||||
uv: string;
|
|
||||||
zlib: string;
|
|
||||||
openssl: string;
|
|
||||||
modules: string;
|
|
||||||
chrome: string;
|
|
||||||
files: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getChromeVersionForElectronVersion(
|
|
||||||
electronVersion: string,
|
|
||||||
url = ELECTRON_VERSIONS_URL,
|
|
||||||
): Promise<string> {
|
|
||||||
log.debug('Grabbing electron<->chrome versions file from', url);
|
|
||||||
const response = await axios.get(url, { timeout: 5000 });
|
|
||||||
if (response.status !== 200) {
|
|
||||||
throw new Error(`Bad request: Status code ${response.status}`);
|
|
||||||
}
|
|
||||||
const electronReleases: ElectronRelease[] = response.data;
|
|
||||||
const electronVersionToChromeVersion: { [key: string]: string } = {};
|
|
||||||
for (const release of electronReleases) {
|
|
||||||
electronVersionToChromeVersion[release.version] = release.chrome;
|
|
||||||
}
|
|
||||||
if (!(electronVersion in electronVersionToChromeVersion)) {
|
|
||||||
throw new Error(
|
|
||||||
`Electron version '${electronVersion}' not found in retrieved version list!`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const chromeVersion = electronVersionToChromeVersion[electronVersion];
|
|
||||||
log.debug(
|
|
||||||
`Associated electron v${electronVersion} to chrome v${chromeVersion}`,
|
|
||||||
);
|
|
||||||
return chromeVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserAgentString(
|
|
||||||
chromeVersion: string,
|
|
||||||
platform: string,
|
|
||||||
): string {
|
|
||||||
let userAgent: string;
|
|
||||||
switch (platform) {
|
|
||||||
case 'darwin':
|
|
||||||
case 'mas':
|
|
||||||
userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
|
||||||
break;
|
|
||||||
case 'win32':
|
|
||||||
userAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
'Error invalid platform specified to getUserAgentString()',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
log.debug(
|
|
||||||
`Given chrome ${chromeVersion} on ${platform},`,
|
|
||||||
`using user agent: ${userAgent}`,
|
|
||||||
);
|
|
||||||
return userAgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function inferUserAgent(
|
|
||||||
electronVersion: string,
|
|
||||||
platform: string,
|
|
||||||
url = ELECTRON_VERSIONS_URL,
|
|
||||||
): Promise<string> {
|
|
||||||
log.debug(
|
|
||||||
`Inferring user agent for electron ${electronVersion} / ${platform}`,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const chromeVersion = await getChromeVersionForElectronVersion(
|
|
||||||
electronVersion,
|
|
||||||
url,
|
|
||||||
);
|
|
||||||
return getUserAgentString(chromeVersion, platform);
|
|
||||||
} catch (e) {
|
|
||||||
log.warn(
|
|
||||||
`Unable to infer chrome version for user agent, using ${DEFAULT_CHROME_VERSION}`,
|
|
||||||
);
|
|
||||||
return getUserAgentString(DEFAULT_CHROME_VERSION, platform);
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,9 +4,12 @@ import * as path from 'path';
|
|||||||
|
|
||||||
import { DEFAULT_ELECTRON_VERSION } from './constants';
|
import { DEFAULT_ELECTRON_VERSION } from './constants';
|
||||||
import { getTempDir } from './helpers/helpers';
|
import { getTempDir } from './helpers/helpers';
|
||||||
|
import { getChromeVersionForElectronVersion } from './infer/browsers/inferChromeVersion';
|
||||||
|
import { getLatestFirefoxVersion } from './infer/browsers/inferFirefoxVersion';
|
||||||
|
import { getLatestSafariVersion } from './infer/browsers/inferSafariVersion';
|
||||||
import { inferArch } from './infer/inferOs';
|
import { inferArch } from './infer/inferOs';
|
||||||
import { inferUserAgent } from './infer/inferUserAgent';
|
|
||||||
import { buildNativefierApp } from './main';
|
import { buildNativefierApp } from './main';
|
||||||
|
import { userAgent } from './options/fields/userAgent';
|
||||||
|
|
||||||
async function checkApp(appRoot: string, inputOptions: any): Promise<void> {
|
async function checkApp(appRoot: string, inputOptions: any): Promise<void> {
|
||||||
const arch = (inputOptions.arch as string) || inferArch();
|
const arch = (inputOptions.arch as string) || inferArch();
|
||||||
@ -66,14 +69,18 @@ async function checkApp(appRoot: string, inputOptions: any): Promise<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test user agent
|
// Test user agent
|
||||||
expect(nativefierConfig.userAgent).toBe(
|
if (inputOptions.userAgent) {
|
||||||
inputOptions.userAgent !== undefined
|
const translatedUserAgent = await userAgent({
|
||||||
? inputOptions.userAgent
|
packager: {
|
||||||
: await inferUserAgent(
|
platform: inputOptions.platform,
|
||||||
|
electronVersion:
|
||||||
inputOptions.electronVersion || DEFAULT_ELECTRON_VERSION,
|
inputOptions.electronVersion || DEFAULT_ELECTRON_VERSION,
|
||||||
inputOptions.platform,
|
},
|
||||||
),
|
nativefier: { userAgent: inputOptions.userAgent },
|
||||||
);
|
});
|
||||||
|
inputOptions.userAgent = translatedUserAgent || inputOptions.userAgent;
|
||||||
|
}
|
||||||
|
expect(nativefierConfig.userAgent).toBe(inputOptions.userAgent);
|
||||||
|
|
||||||
// Test lang
|
// Test lang
|
||||||
expect(nativefierConfig.lang).toBe(inputOptions.lang);
|
expect(nativefierConfig.lang).toBe(inputOptions.lang);
|
||||||
@ -87,10 +94,10 @@ describe('Nativefier', () => {
|
|||||||
async (platform) => {
|
async (platform) => {
|
||||||
const tempDirectory = getTempDir('integtest');
|
const tempDirectory = getTempDir('integtest');
|
||||||
const options = {
|
const options = {
|
||||||
|
platform,
|
||||||
targetUrl: 'https://google.com/',
|
targetUrl: 'https://google.com/',
|
||||||
out: tempDirectory,
|
out: tempDirectory,
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
platform,
|
|
||||||
lang: 'en-US',
|
lang: 'en-US',
|
||||||
};
|
};
|
||||||
const appPath = await buildNativefierApp(options);
|
const appPath = await buildNativefierApp(options);
|
||||||
@ -104,7 +111,7 @@ describe('Nativefier upgrade', () => {
|
|||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ platform: 'darwin', arch: 'x64' },
|
{ platform: 'darwin', arch: 'x64' },
|
||||||
{ platform: 'linux', arch: 'arm64', userAgent: 'FIREFOX' },
|
{ platform: 'linux', arch: 'arm64', userAgent: 'FIREFOX 60' },
|
||||||
// Exhaustive integration testing here would be neat, but takes too long.
|
// Exhaustive integration testing here would be neat, but takes too long.
|
||||||
// -> For now, only testing a subset of platforms/archs
|
// -> For now, only testing a subset of platforms/archs
|
||||||
// { platform: 'win32', arch: 'x64' },
|
// { platform: 'win32', arch: 'x64' },
|
||||||
@ -134,14 +141,29 @@ describe('Nativefier upgrade', () => {
|
|||||||
|
|
||||||
const upgradeAppPath = await buildNativefierApp(upgradeOptions);
|
const upgradeAppPath = await buildNativefierApp(upgradeOptions);
|
||||||
options.electronVersion = DEFAULT_ELECTRON_VERSION;
|
options.electronVersion = DEFAULT_ELECTRON_VERSION;
|
||||||
options.userAgent =
|
options.userAgent = baseAppOptions.userAgent;
|
||||||
baseAppOptions.userAgent !== undefined
|
|
||||||
? baseAppOptions.userAgent
|
|
||||||
: await inferUserAgent(
|
|
||||||
DEFAULT_ELECTRON_VERSION,
|
|
||||||
baseAppOptions.platform,
|
|
||||||
);
|
|
||||||
await checkApp(upgradeAppPath, options);
|
await checkApp(upgradeAppPath, options);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Browser version retrieval', () => {
|
||||||
|
test('get chrome version with electron version', async () => {
|
||||||
|
await expect(getChromeVersionForElectronVersion('12.0.0')).resolves.toBe(
|
||||||
|
'89.0.4389.69',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get latest firefox version', async () => {
|
||||||
|
const firefoxVersion = await getLatestFirefoxVersion();
|
||||||
|
|
||||||
|
const majorVersion = parseInt(firefoxVersion.split('.')[0]);
|
||||||
|
expect(majorVersion).toBeGreaterThanOrEqual(88);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get latest safari version', async () => {
|
||||||
|
const safariVersion = await getLatestSafariVersion();
|
||||||
|
|
||||||
|
expect(safariVersion.majorVersion).toBeGreaterThanOrEqual(14);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -18,7 +18,7 @@ test('fully-defined async options are returned as-is', async () => {
|
|||||||
expect(options.nativefier.userAgent).toEqual('random user agent');
|
expect(options.nativefier.userAgent).toEqual('random user agent');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('user agent is inferred if not passed', async () => {
|
test('user agent is ignored if not provided', async () => {
|
||||||
const options = {
|
const options = {
|
||||||
packager: {
|
packager: {
|
||||||
icon: '/my/icon.png',
|
icon: '/my/icon.png',
|
||||||
@ -32,5 +32,22 @@ test('user agent is inferred if not passed', async () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await processOptions(options);
|
await processOptions(options);
|
||||||
|
|
||||||
expect(options.nativefier.userAgent).toMatch(/Linux.*Chrome/);
|
expect(options.nativefier.userAgent).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('user agent short code is populated', async () => {
|
||||||
|
const options = {
|
||||||
|
packager: {
|
||||||
|
icon: '/my/icon.png',
|
||||||
|
name: 'my beautiful app ',
|
||||||
|
targetUrl: 'https://myurl.com',
|
||||||
|
dir: '/tmp/myapp',
|
||||||
|
platform: 'linux',
|
||||||
|
},
|
||||||
|
nativefier: { userAgent: 'edge' },
|
||||||
|
};
|
||||||
|
// @ts-ignore
|
||||||
|
await processOptions(options);
|
||||||
|
|
||||||
|
expect(options.nativefier.userAgent).not.toBe('edge');
|
||||||
});
|
});
|
||||||
|
@ -1,26 +1,90 @@
|
|||||||
|
import { getChromeVersionForElectronVersion } from '../../infer/browsers/inferChromeVersion';
|
||||||
|
import { getLatestFirefoxVersion } from '../../infer/browsers/inferFirefoxVersion';
|
||||||
|
import { getLatestSafariVersion } from '../../infer/browsers/inferSafariVersion';
|
||||||
import { userAgent } from './userAgent';
|
import { userAgent } from './userAgent';
|
||||||
import { inferUserAgent } from '../../infer/inferUserAgent';
|
|
||||||
|
|
||||||
jest.mock('./../../infer/inferUserAgent');
|
jest.mock('./../../infer/browsers/inferChromeVersion');
|
||||||
|
jest.mock('./../../infer/browsers/inferFirefoxVersion');
|
||||||
|
jest.mock('./../../infer/browsers/inferSafariVersion');
|
||||||
|
|
||||||
test('when a userAgent parameter is passed', async () => {
|
test('when a userAgent parameter is passed', async () => {
|
||||||
expect(inferUserAgent).toHaveBeenCalledTimes(0);
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
packager: {},
|
packager: {},
|
||||||
nativefier: { userAgent: 'valid user agent' },
|
nativefier: { userAgent: 'valid user agent' },
|
||||||
};
|
};
|
||||||
await expect(userAgent(params)).resolves.toBe(null);
|
await expect(userAgent(params)).resolves.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('no userAgent parameter is passed', async () => {
|
test('no userAgent parameter is passed', async () => {
|
||||||
const params = {
|
const params = {
|
||||||
packager: { electronVersion: '123', platform: 'mac' },
|
packager: { platform: 'mac' },
|
||||||
nativefier: {},
|
nativefier: {},
|
||||||
};
|
};
|
||||||
await userAgent(params);
|
await expect(userAgent(params)).resolves.toBeNull();
|
||||||
expect(inferUserAgent).toHaveBeenCalledWith(
|
});
|
||||||
params.packager.electronVersion,
|
|
||||||
params.packager.platform,
|
test('edge userAgent parameter is passed', async () => {
|
||||||
);
|
(getChromeVersionForElectronVersion as jest.Mock).mockImplementationOnce(() =>
|
||||||
|
Promise.resolve('99.0.0'),
|
||||||
|
);
|
||||||
|
const params = {
|
||||||
|
packager: { platform: 'darwin' },
|
||||||
|
nativefier: { userAgent: 'edge' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedUserAgent = await userAgent(params);
|
||||||
|
|
||||||
|
expect(parsedUserAgent).not.toBe(params.nativefier.userAgent);
|
||||||
|
expect(parsedUserAgent).toContain('Edg/99.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('firefox userAgent parameter is passed', async () => {
|
||||||
|
(getLatestFirefoxVersion as jest.Mock).mockImplementationOnce(() =>
|
||||||
|
Promise.resolve('100.0.0'),
|
||||||
|
);
|
||||||
|
const params = {
|
||||||
|
packager: { platform: 'win32' },
|
||||||
|
nativefier: { userAgent: 'firefox' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedUserAgent = await userAgent(params);
|
||||||
|
|
||||||
|
expect(parsedUserAgent).not.toBe(params.nativefier.userAgent);
|
||||||
|
expect(parsedUserAgent).toContain('Firefox/100.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('safari userAgent parameter is passed', async () => {
|
||||||
|
(getLatestSafariVersion as jest.Mock).mockImplementationOnce(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
majorVersion: 101,
|
||||||
|
version: '101.0.0',
|
||||||
|
webkitVersion: '600.0.0.0',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const params = {
|
||||||
|
packager: { platform: 'linux' },
|
||||||
|
nativefier: { userAgent: 'safari' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedUserAgent = await userAgent(params);
|
||||||
|
|
||||||
|
expect(parsedUserAgent).not.toBe(params.nativefier.userAgent);
|
||||||
|
expect(parsedUserAgent).toContain('Version/101.0.0 Safari');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('short userAgent parameter is passed with an electronVersion', async () => {
|
||||||
|
(getChromeVersionForElectronVersion as jest.Mock).mockImplementationOnce(() =>
|
||||||
|
Promise.resolve('102.0.0'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
packager: { electronVersion: '12.0.0', platform: 'darwin' },
|
||||||
|
nativefier: { userAgent: 'edge' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedUserAgent = await userAgent(params);
|
||||||
|
|
||||||
|
expect(parsedUserAgent).not.toBe(params.nativefier.userAgent);
|
||||||
|
expect(parsedUserAgent).toContain('102.0.0');
|
||||||
|
expect(getChromeVersionForElectronVersion).toHaveBeenCalledWith('12.0.0');
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { inferUserAgent } from '../../infer/inferUserAgent';
|
import * as log from 'loglevel';
|
||||||
|
|
||||||
|
import { getChromeVersionForElectronVersion } from '../../infer/browsers/inferChromeVersion';
|
||||||
|
import { getLatestFirefoxVersion } from '../../infer/browsers/inferFirefoxVersion';
|
||||||
|
import { getLatestSafariVersion } from '../../infer/browsers/inferSafariVersion';
|
||||||
|
import { normalizePlatform } from '../optionsMain';
|
||||||
|
|
||||||
type UserAgentOpts = {
|
type UserAgentOpts = {
|
||||||
packager: {
|
packager: {
|
||||||
@ -10,13 +15,68 @@ type UserAgentOpts = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const USER_AGENT_PLATFORM_MAPS = {
|
||||||
|
darwin: 'Macintosh; Intel Mac OS X 10_15_7',
|
||||||
|
linux: 'X11; Linux x86_64',
|
||||||
|
win32: 'Windows NT 10.0; Win64; x64',
|
||||||
|
};
|
||||||
|
|
||||||
|
const USER_AGENT_SHORT_CODE_MAPS = {
|
||||||
|
edge: edgeUserAgent,
|
||||||
|
firefox: firefoxUserAgent,
|
||||||
|
safari: safariUserAgent,
|
||||||
|
};
|
||||||
|
|
||||||
export async function userAgent(options: UserAgentOpts): Promise<string> {
|
export async function userAgent(options: UserAgentOpts): Promise<string> {
|
||||||
if (options.nativefier.userAgent) {
|
if (!options.nativefier.userAgent) {
|
||||||
|
// No user agent got passed. Let's handle it with the app.userAgentFallback
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inferUserAgent(
|
if (
|
||||||
options.packager.electronVersion,
|
!Object.keys(USER_AGENT_SHORT_CODE_MAPS).includes(
|
||||||
options.packager.platform,
|
options.nativefier.userAgent.toLowerCase(),
|
||||||
);
|
)
|
||||||
|
) {
|
||||||
|
// Real user agent got passed. No need to translate it.
|
||||||
|
log.debug(
|
||||||
|
`${options.nativefier.userAgent.toLowerCase()} not found in`,
|
||||||
|
Object.keys(USER_AGENT_SHORT_CODE_MAPS),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.packager.platform = normalizePlatform(options.packager.platform);
|
||||||
|
|
||||||
|
const userAgentPlatform =
|
||||||
|
USER_AGENT_PLATFORM_MAPS[
|
||||||
|
options.packager.platform === 'mas' ? 'darwin' : options.packager.platform
|
||||||
|
];
|
||||||
|
|
||||||
|
const mapFunction = USER_AGENT_SHORT_CODE_MAPS[options.nativefier.userAgent];
|
||||||
|
|
||||||
|
return await mapFunction(userAgentPlatform, options.packager.electronVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function edgeUserAgent(
|
||||||
|
platform: string,
|
||||||
|
electronVersion: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const chromeVersion = await getChromeVersionForElectronVersion(
|
||||||
|
electronVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
return `Mozilla/5.0 (${platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36 Edg/${chromeVersion}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function firefoxUserAgent(platform: string): Promise<string> {
|
||||||
|
const firefoxVersion = await getLatestFirefoxVersion();
|
||||||
|
|
||||||
|
return `Mozilla/5.0 (${platform}; rv:${firefoxVersion}) Gecko/20100101 Firefox/${firefoxVersion}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function safariUserAgent(platform: string): Promise<string> {
|
||||||
|
const safariVersion = await getLatestSafariVersion();
|
||||||
|
|
||||||
|
return `Mozilla/5.0 (${platform}) AppleWebKit/${safariVersion.webkitVersion} (KHTML, like Gecko) Version/${safariVersion.version} Safari/${safariVersion.webkitVersion}`;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ export interface AppOptions {
|
|||||||
fullScreen: boolean;
|
fullScreen: boolean;
|
||||||
globalShortcuts: any;
|
globalShortcuts: any;
|
||||||
hideWindowFrame: boolean;
|
hideWindowFrame: boolean;
|
||||||
honest: boolean;
|
|
||||||
ignoreCertificate: boolean;
|
ignoreCertificate: boolean;
|
||||||
ignoreGpuBlacklist: boolean;
|
ignoreGpuBlacklist: boolean;
|
||||||
inject: string[];
|
inject: string[];
|
||||||
@ -52,7 +51,7 @@ export interface AppOptions {
|
|||||||
titleBarStyle: string;
|
titleBarStyle: string;
|
||||||
tray: string | boolean;
|
tray: string | boolean;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
userAgentOverriden: boolean;
|
userAgentHonest: boolean;
|
||||||
verbose: boolean;
|
verbose: boolean;
|
||||||
versionString: string;
|
versionString: string;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { getOptions } from './optionsMain';
|
import { getOptions, normalizePlatform } from './optionsMain';
|
||||||
import * as asyncConfig from './asyncConfig';
|
import * as asyncConfig from './asyncConfig';
|
||||||
|
import { inferPlatform } from '../infer/inferOs';
|
||||||
|
|
||||||
const mockedAsyncConfig = { some: 'options' };
|
const mockedAsyncConfig = { some: 'options' };
|
||||||
let asyncConfigMock: jasmine.Spy;
|
let asyncConfigMock: jasmine.Spy;
|
||||||
@ -38,3 +39,21 @@ test('it should set the accessibility prompt option to true by default', async (
|
|||||||
);
|
);
|
||||||
expect(result.nativefier.accessibilityPrompt).toEqual(true);
|
expect(result.nativefier.accessibilityPrompt).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{ platform: 'darwin', expectedPlatform: 'darwin' },
|
||||||
|
{ platform: 'mAc', expectedPlatform: 'darwin' },
|
||||||
|
{ platform: 'osx', expectedPlatform: 'darwin' },
|
||||||
|
{ platform: 'liNux', expectedPlatform: 'linux' },
|
||||||
|
{ platform: 'mas', expectedPlatform: 'mas' },
|
||||||
|
{ platform: 'WIN32', expectedPlatform: 'win32' },
|
||||||
|
{ platform: 'windows', expectedPlatform: 'win32' },
|
||||||
|
{},
|
||||||
|
])('it should be able to normalize the platform %s', (platformOptions) => {
|
||||||
|
if (!platformOptions.expectedPlatform) {
|
||||||
|
platformOptions.expectedPlatform = inferPlatform();
|
||||||
|
}
|
||||||
|
expect(normalizePlatform(platformOptions.platform)).toBe(
|
||||||
|
platformOptions.expectedPlatform,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@ -37,7 +37,7 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
name: typeof rawOptions.name === 'string' ? rawOptions.name : '',
|
name: typeof rawOptions.name === 'string' ? rawOptions.name : '',
|
||||||
out: rawOptions.out || process.cwd(),
|
out: rawOptions.out || process.cwd(),
|
||||||
overwrite: rawOptions.overwrite,
|
overwrite: rawOptions.overwrite,
|
||||||
platform: rawOptions.platform || inferPlatform(),
|
platform: rawOptions.platform,
|
||||||
portable: rawOptions.portable || false,
|
portable: rawOptions.portable || false,
|
||||||
targetUrl:
|
targetUrl:
|
||||||
rawOptions.targetUrl === undefined
|
rawOptions.targetUrl === undefined
|
||||||
@ -78,7 +78,6 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
fullScreen: rawOptions.fullScreen || false,
|
fullScreen: rawOptions.fullScreen || false,
|
||||||
globalShortcuts: null,
|
globalShortcuts: null,
|
||||||
hideWindowFrame: rawOptions.hideWindowFrame,
|
hideWindowFrame: rawOptions.hideWindowFrame,
|
||||||
honest: rawOptions.honest || false,
|
|
||||||
ignoreCertificate: rawOptions.ignoreCertificate || false,
|
ignoreCertificate: rawOptions.ignoreCertificate || false,
|
||||||
ignoreGpuBlacklist: rawOptions.ignoreGpuBlacklist || false,
|
ignoreGpuBlacklist: rawOptions.ignoreGpuBlacklist || false,
|
||||||
inject: rawOptions.inject || [],
|
inject: rawOptions.inject || [],
|
||||||
@ -94,8 +93,7 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
titleBarStyle: rawOptions.titleBarStyle || null,
|
titleBarStyle: rawOptions.titleBarStyle || null,
|
||||||
tray: rawOptions.tray || false,
|
tray: rawOptions.tray || false,
|
||||||
userAgent: rawOptions.userAgent,
|
userAgent: rawOptions.userAgent,
|
||||||
userAgentOverriden:
|
userAgentHonest: rawOptions.userAgentHonest || false,
|
||||||
rawOptions.userAgent !== undefined && rawOptions.userAgent !== null,
|
|
||||||
verbose: rawOptions.verbose,
|
verbose: rawOptions.verbose,
|
||||||
versionString: rawOptions.versionString,
|
versionString: rawOptions.versionString,
|
||||||
width: rawOptions.width || 1280,
|
width: rawOptions.width || 1280,
|
||||||
@ -174,18 +172,14 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
options.nativefier.insecure = true;
|
options.nativefier.insecure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.nativefier.honest) {
|
if (options.nativefier.userAgentHonest && options.nativefier.userAgent) {
|
||||||
options.nativefier.userAgent = null;
|
options.nativefier.userAgent = null;
|
||||||
|
log.warn(
|
||||||
|
`\nATTENTION: user-agent AND user-agent-honest/honest were provided. In this case, honesty wins. user-agent will be ignored`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const platform = options.packager.platform.toLowerCase();
|
options.packager.platform = normalizePlatform(options.packager.platform);
|
||||||
if (platform === 'windows') {
|
|
||||||
options.packager.platform = 'win32';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['osx', 'mac', 'macos'].includes(platform)) {
|
|
||||||
options.packager.platform = 'darwin';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.nativefier.width > options.nativefier.maxWidth) {
|
if (options.nativefier.width > options.nativefier.maxWidth) {
|
||||||
options.nativefier.width = options.nativefier.maxWidth;
|
options.nativefier.width = options.nativefier.maxWidth;
|
||||||
@ -218,3 +212,18 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizePlatform(platform: string): string {
|
||||||
|
if (!platform) {
|
||||||
|
return inferPlatform();
|
||||||
|
}
|
||||||
|
if (platform.toLowerCase() === 'windows') {
|
||||||
|
return 'win32';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['osx', 'mac', 'macos'].includes(platform.toLowerCase())) {
|
||||||
|
return 'darwin';
|
||||||
|
}
|
||||||
|
|
||||||
|
return platform.toLowerCase();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user