136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
import type { WebContents, BrowserWindowConstructorOptions, LoadURLOptions } from 'electron';
|
|
import { promisify } from 'util';
|
|
import { AppWindow } from './app-window';
|
|
import type { UrlAppWindowInterface } from './url-app-window-interface';
|
|
import Timeout = NodeJS.Timeout;
|
|
|
|
export abstract class UrlAppWindow extends AppWindow implements UrlAppWindowInterface {
|
|
protected loadOptions: LoadURLOptions;
|
|
|
|
protected readyCheck?: (webContents: WebContents) => Promise<boolean>;
|
|
|
|
/**
|
|
* the wait interval after a failed load to try again
|
|
*/
|
|
private waitInterval: number = Milliseconds.TWO_HUNDRED;
|
|
|
|
private loadWaitTime: number = 0;
|
|
|
|
private loadWaitTimeStep: number = 10;
|
|
|
|
private loadWaitTimeStepResetTimeout: Timeout;
|
|
|
|
private loadWaitTimeResetTimeoutTime: number = Milliseconds.ONE_MINUTE;
|
|
|
|
/**
|
|
* when this promise is resolved a safe load is allowed
|
|
*
|
|
* it resets which each load to resolve again after a wait time
|
|
*
|
|
* @see loadWaitTime
|
|
*/
|
|
private loadWait: Promise<void> = Promise.resolve();
|
|
|
|
protected constructor(
|
|
logger: LoggerInterface,
|
|
uri: string,
|
|
options: BrowserWindowConstructorOptions = {},
|
|
loadOptions: LoadURLOptions = {},
|
|
) {
|
|
super(logger, uri, {
|
|
...options,
|
|
...{
|
|
webPreferences: {
|
|
enableRemoteModule: false,
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
},
|
|
},
|
|
});
|
|
this.loadOptions = loadOptions;
|
|
this.loadWaitTimeStepResetTimeout = setTimeout(() => {}, 0);
|
|
}
|
|
|
|
public downloadUrlSafe(url: string, savePath: string, options?: LoadURLOptions): Promise<void> {
|
|
return new Promise<void>((resolve, reject) => {
|
|
this.getWindow().webContents.session.once('will-download', (event, item) => {
|
|
item.setSavePath(savePath);
|
|
item.once('done', (doneEvent, state) => {
|
|
switch (state) {
|
|
case 'completed':
|
|
resolve();
|
|
break;
|
|
case 'cancelled':
|
|
case 'interrupted':
|
|
default:
|
|
reject(new Error(state));
|
|
break;
|
|
}
|
|
});
|
|
item.on('updated', () => {
|
|
item.resume();
|
|
});
|
|
});
|
|
void this.loadUrlSafe(url, undefined, options);
|
|
});
|
|
}
|
|
|
|
public async loadUrlSafe(
|
|
url: string,
|
|
readyCheck?: (webContents: WebContents) => Promise<boolean>,
|
|
options?: LoadURLOptions,
|
|
): Promise<void> {
|
|
await this.loadWait;
|
|
let failedLoad = true;
|
|
while (failedLoad) {
|
|
const httpResponseCode = await this.loadUrl(url, options);
|
|
failedLoad = HttpCode.BAD_REQUEST <= httpResponseCode;
|
|
switch (httpResponseCode) {
|
|
case HttpCode.NOT_FOUND:
|
|
throw new Error(`Loading ${url} gave the unrecoverable HTTP status code ${httpResponseCode}.`);
|
|
case HttpCode.TOO_MANY_REQUESTS:
|
|
// go slower
|
|
this.loadWaitTime += this.loadWaitTimeStep;
|
|
// but go faster again after a time
|
|
clearTimeout(this.loadWaitTimeStepResetTimeout);
|
|
this.loadWaitTimeStepResetTimeout = setTimeout(() => {
|
|
this.loadWaitTime = 0;
|
|
}, this.loadWaitTimeResetTimeoutTime);
|
|
break;
|
|
}
|
|
if (failedLoad) {
|
|
await promisify(setTimeout)(this.waitInterval);
|
|
}
|
|
}
|
|
this.loadWait = promisify(setTimeout)(this.loadWaitTime);
|
|
if (readyCheck) {
|
|
let isReady = await readyCheck(this.getWindow().webContents);
|
|
do {
|
|
await promisify(setTimeout)(Milliseconds.TEN);
|
|
isReady = await readyCheck(this.getWindow().webContents);
|
|
} while (!isReady);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is the method used for loading specific URLs.
|
|
* It resolves when the url is loaded, successfully or not-
|
|
*
|
|
* It is meant to be overridden for site specific logic, e.g. C l o u d f l a r e
|
|
*
|
|
* @return a Promise of the http status code the url loaded with
|
|
*/
|
|
protected loadUrl(url: string, options?: LoadURLOptions): Promise<number> {
|
|
return new Promise((resolve) => {
|
|
this.getWindow().webContents.once('did-navigate', (event, navigationUrl, httpResponseCode) => {
|
|
resolve(httpResponseCode);
|
|
});
|
|
void this.getWindow().loadURL(url, options);
|
|
});
|
|
}
|
|
|
|
protected load(): Promise<void> {
|
|
return this.loadUrlSafe(this.uri, this.readyCheck, this.loadOptions).then();
|
|
}
|
|
}
|