function configureStartupRenderer(app, env = process.env, platform = process.platform) {
const sessionName = String(env && env.SESSIONNAME || '');
if (platform === 'win32' && /^RDP-/i.test(sessionName)) app.disableHardwareAcceleration();
return app;
}
function resolveStartupLanguage(config) {
return config && config.globalSettings && config.globalSettings.language === 'de' ? 'de' : 'en';
}
function createStartupFailureDocument(language) {
const german = language === 'de';
const title = german ? 'Oberfläche konnte nicht geladen werden' : 'The interface could not load';
const detail = german
? 'Multi Hoster Uploader konnte die Oberfläche nach einem sicheren Wiederherstellungsversuch nicht laden.'
: 'Multi Hoster Uploader could not load the interface after a safe recovery attempt.';
const close = german ? 'Schließen' : 'Close';
return `
Multi Hoster Uploader ${title} ${detail}
${close} `;
}
function createStartupNavigationLoader(window, target, options = {}) {
let startupDocument = 0;
return function loadStartupDocument() {
startupDocument++;
return window.loadFile(target, {
...options,
query: {
...options.query,
startupDocument: String(startupDocument)
}
});
};
}
function createStartupRevealGate(window, { onBlock } = {}) {
let blocked = true;
let activationPending = false;
function hasWindow() {
return !!window && (typeof window.isDestroyed !== 'function' || !window.isDestroyed());
}
function showAuthorizedSurface(activate) {
if (!hasWindow()) return false;
if (activate && window.isMinimized()) window.restore();
if (!window.isVisible()) window.show();
if (activate) window.focus();
activationPending = false;
return true;
}
const block = () => {
blocked = true;
if (typeof onBlock === 'function') onBlock();
return true;
};
const request = () => {
if (!hasWindow()) return false;
activationPending = true;
if (!blocked) showAuthorizedSurface(true);
return true;
};
const reveal = () => {
blocked = false;
return showAuthorizedSurface(activationPending);
};
const navigate = (operation, ...args) => {
block();
return operation(...args);
};
return { block, navigate, request, reveal };
}
function createStartupExternalRevealBindings({
getWindow,
getRevealGate,
sendDroppedFiles,
maxPendingDropPayloads = 32
}) {
const pendingDropPayloads = [];
const pendingDropLimit = Number.isSafeInteger(maxPendingDropPayloads) && maxPendingDropPayloads > 0
? maxPendingDropPayloads
: 32;
let pendingWindow = null;
let readyWindow = null;
function clearPendingDropPayloads(window) {
if (window && pendingWindow !== window) return false;
pendingDropPayloads.length = 0;
pendingWindow = null;
return true;
}
function clearWindowState(window) {
clearPendingDropPayloads(window);
if (!window || readyWindow === window) readyWindow = null;
}
function getActiveWindow() {
const window = getWindow();
if (!window || (typeof window.isDestroyed === 'function' && window.isDestroyed())) {
clearWindowState();
return null;
}
if (pendingWindow && pendingWindow !== window) clearPendingDropPayloads();
if (readyWindow && readyWindow !== window) readyWindow = null;
return window;
}
function requestReveal() {
const activeWindow = getActiveWindow();
if (!activeWindow) return false;
const revealGate = getRevealGate();
if (!revealGate || typeof revealGate.request !== 'function') return false;
revealGate.request();
return true;
}
function queueDropPayload(window, paths) {
if (pendingWindow && pendingWindow !== window) clearPendingDropPayloads();
pendingWindow = window;
if (pendingDropPayloads.length >= pendingDropLimit) pendingDropPayloads.shift();
pendingDropPayloads.push(paths);
}
function handleDropTargetFiles(_event, paths) {
const window = getActiveWindow();
if (!window) return false;
if (!window.isVisible() || window.isMinimized()) requestReveal();
if (readyWindow === window) sendDroppedFiles(paths);
else queueDropPayload(window, paths);
return true;
}
function rendererBlocked(window) {
const activeWindow = getActiveWindow();
if (!activeWindow || activeWindow !== window) {
clearWindowState(window);
return false;
}
readyWindow = null;
return true;
}
function rendererReady(window) {
const activeWindow = getActiveWindow();
if (!activeWindow || activeWindow !== window) {
clearWindowState(window);
return false;
}
readyWindow = window;
if (pendingWindow !== window) return true;
const payloads = pendingDropPayloads.splice(0);
pendingWindow = null;
for (const paths of payloads) sendDroppedFiles(paths);
return true;
}
function windowClosed(window) {
clearWindowState(window);
}
return {
bindSecondInstance(app) {
app.on('second-instance', requestReveal);
},
bindTrayClick(tray) {
tray.on('click', requestReveal);
},
createTrayMenuItem(label) {
return { label, click: requestReveal };
},
bindDropTargetFiles(ipcMain) {
ipcMain.on('drop-target:files', handleDropTargetFiles);
},
rendererBlocked,
rendererReady,
windowClosed
};
}
function createStartupCloseHandler({ window, shouldPrepareClose, requestClosePreparation }) {
return function handleStartupClose(event) {
if (!shouldPrepareClose() || window.webContents.isDestroyed()) return false;
event.preventDefault();
requestClosePreparation();
return true;
};
}
function createStartupRecoveryCoordinator({
load,
reload,
reveal,
showFailure,
close,
readyTimeoutMs = 15000,
scheduleReadyDeadline = setTimeout,
cancelReadyDeadline = clearTimeout
}) {
let initialLoad;
let initialLoadPending = false;
let navigation;
let recoveryNavigations = 0;
let terminalFailure;
let recovery;
let queuedRecovery;
let readyDeadline;
let rendererGeneration = 0;
let awaitingGeneration = null;
let stopped = false;
function clearRendererDeadline() {
if (readyDeadline !== undefined) cancelReadyDeadline(readyDeadline);
readyDeadline = undefined;
}
function clearRendererDocument() {
awaitingGeneration = null;
clearRendererDeadline();
}
function acceptRendererDocument(generation) {
if (!Number.isInteger(generation) || awaitingGeneration !== generation) return false;
clearRendererDocument();
return true;
}
async function runNavigation(operation, args = []) {
if (navigation) return navigation;
const currentNavigation = Promise.resolve().then(() => operation(...args));
navigation = currentNavigation;
try {
return await currentNavigation;
} finally {
if (navigation === currentNavigation) navigation = undefined;
}
}
function endWithFailure(failure) {
if (stopped) return Promise.resolve(false);
if (!terminalFailure) {
clearRendererDocument();
terminalFailure = Promise.resolve().then(async () => {
if (typeof showFailure !== 'function') {
await close(failure);
return;
}
try {
await showFailure(failure);
reveal();
} catch (surfaceError) {
await close({ ...failure, surfaceError });
}
});
}
return terminalFailure;
}
function trackRecovery(currentRecovery) {
recovery = currentRecovery;
currentRecovery.then(
() => {
if (recovery === currentRecovery) recovery = undefined;
},
() => {
if (recovery === currentRecovery) recovery = undefined;
}
);
return currentRecovery;
}
async function performRecovery(phase, details) {
if (stopped) return false;
if (terminalFailure) return terminalFailure;
if (recoveryNavigations >= 1) {
return endWithFailure({ phase, attempt: recoveryNavigations + 1, details });
}
recoveryNavigations++;
try {
await runNavigation(reload);
return true;
} catch (error) {
if (stopped) return false;
return endWithFailure({ phase: 'renderer-reload', attempt: recoveryNavigations, details, error });
}
}
function recoverRenderer(phase, details) {
if (stopped) return Promise.resolve(false);
if (terminalFailure) return terminalFailure;
clearRendererDocument();
if (initialLoadPending) {
queuedRecovery = { phase, details, recoveryNavigations };
if (recovery) return recovery;
const currentRecovery = Promise.resolve(initialLoad).then(() => {
const pendingRecovery = queuedRecovery;
queuedRecovery = undefined;
if (stopped) return false;
if (terminalFailure) return terminalFailure;
if (!pendingRecovery || recoveryNavigations > pendingRecovery.recoveryNavigations) return true;
return performRecovery(pendingRecovery.phase, pendingRecovery.details);
});
return trackRecovery(currentRecovery);
}
if (recovery) return recovery;
return trackRecovery(performRecovery(phase, details));
}
return {
loadInitial(...args) {
if (stopped) return Promise.resolve(false);
if (terminalFailure) return terminalFailure;
if (!initialLoad) {
initialLoadPending = true;
const currentInitialLoad = (async () => {
for (let attempt = 1; attempt <= 2; attempt++) {
if (stopped) return false;
if (attempt === 2) recoveryNavigations = Math.max(recoveryNavigations, 1);
try {
return await runNavigation(load, args);
} catch (error) {
if (stopped) return false;
if (attempt === 2) {
await endWithFailure({ phase: 'initial-load', attempt, error });
}
}
}
})();
initialLoad = currentInitialLoad;
currentInitialLoad.then(
() => {
initialLoadPending = false;
},
() => {
initialLoadPending = false;
}
);
}
return initialLoad;
},
rendererCrashed(details) {
return recoverRenderer('renderer-crash', details);
},
rendererInitializationFailed(details) {
return recoverRenderer('renderer-initialization', details);
},
rendererLoadStarted() {
if (stopped || terminalFailure) return false;
clearRendererDocument();
rendererGeneration++;
awaitingGeneration = rendererGeneration;
return rendererGeneration;
},
rendererLoaded(generation) {
if (stopped || terminalFailure || awaitingGeneration !== generation) return false;
clearRendererDeadline();
readyDeadline = scheduleReadyDeadline(() => {
if (stopped || terminalFailure || awaitingGeneration !== generation) return false;
readyDeadline = undefined;
return recoverRenderer('renderer-ready-timeout', { timeoutMs: readyTimeoutMs });
}, readyTimeoutMs);
if (readyDeadline && typeof readyDeadline.unref === 'function') readyDeadline.unref();
return true;
},
rendererReady(generation) {
if (stopped || terminalFailure || !acceptRendererDocument(generation)) return false;
recoveryNavigations = 0;
reveal();
return true;
},
dispose() {
if (stopped) return false;
stopped = true;
queuedRecovery = undefined;
clearRendererDocument();
return true;
}
};
}
function createStartupRendererHandlers({
window,
ipcMain,
coordinator,
onDocumentLoadStarted,
onRendererCrashed,
onReady,
onInitializationFailed
}) {
let disposed = false;
let generation = null;
let activeFrame = null;
let pendingDocumentUrl = null;
let pendingFrameAddress = null;
const webContents = window && window.webContents;
function frameAddress(frame) {
if (!frame || frame.detached) return null;
if (typeof frame.isDestroyed === 'function' && frame.isDestroyed()) return null;
if (!Number.isInteger(frame.processId) || typeof frame.frameToken !== 'string' || !frame.frameToken) return null;
return `${frame.processId}:${frame.frameToken}`;
}
function frameIdentity(frame) {
const address = frameAddress(frame);
if (!address) return null;
if (typeof frame.url !== 'string' || !frame.url) return null;
return `${address}:${frame.url}`;
}
function startDocument(url, frame) {
if (disposed) return false;
generation = coordinator.rendererLoadStarted();
activeFrame = null;
pendingDocumentUrl = typeof url === 'string' && url ? url : null;
pendingFrameAddress = frameAddress(frame);
if (generation !== false && typeof onDocumentLoadStarted === 'function') onDocumentLoadStarted();
return generation;
}
function finishDocument() {
if (disposed || generation === null) return false;
activeFrame = frameIdentity(webContents && webContents.mainFrame);
return coordinator.rendererLoaded(generation);
}
function resolveEventFrame(event) {
if (disposed || !window || window.isDestroyed() || !event || event.sender !== webContents) return null;
if (!frameIdentity(event.senderFrame)) return null;
return event.senderFrame;
}
function resolveReadyGeneration(event) {
const senderFrame = resolveEventFrame(event);
if (!senderFrame || !Number.isInteger(generation) || !activeFrame || frameIdentity(senderFrame) !== activeFrame) return null;
return generation;
}
function resolveInitializationGeneration(event) {
const senderFrame = resolveEventFrame(event);
if (!senderFrame || !Number.isInteger(generation)) return null;
if (activeFrame) return frameIdentity(senderFrame) === activeFrame ? generation : null;
if (!pendingDocumentUrl || !pendingFrameAddress) return null;
if (senderFrame.url !== pendingDocumentUrl || frameAddress(senderFrame) !== pendingFrameAddress) return null;
return generation;
}
function handleNavigation(details, _url, isInPlace, isMainFrame) {
const sameDocument = details && typeof details.isSameDocument === 'boolean' ? details.isSameDocument : isInPlace;
const mainFrame = details && typeof details.isMainFrame === 'boolean' ? details.isMainFrame : isMainFrame;
if (sameDocument || mainFrame === false) return false;
const url = details && typeof details.url === 'string' ? details.url : _url;
const frame = details && details.frame;
return startDocument(url, frame);
}
function handleRendererCrash(_event, details) {
if (disposed) return false;
if (typeof onRendererCrashed === 'function') onRendererCrashed(details);
return coordinator.rendererCrashed(details);
}
function handleRendererInitializationFailed(event, details) {
if (resolveInitializationGeneration(event) === null) return false;
if (typeof onInitializationFailed === 'function') onInitializationFailed(details);
return coordinator.rendererInitializationFailed(details);
}
function handleRendererReady(event) {
const eventGeneration = resolveReadyGeneration(event);
if (eventGeneration === null) return false;
const ready = coordinator.rendererReady(eventGeneration);
if (ready && typeof onReady === 'function') onReady();
return ready;
}
if (webContents && typeof webContents.on === 'function') {
webContents.on('did-start-navigation', handleNavigation);
webContents.on('did-finish-load', finishDocument);
webContents.on('render-process-gone', handleRendererCrash);
}
if (ipcMain && typeof ipcMain.on === 'function') {
ipcMain.on('app:close-handshake-ready', handleRendererReady);
ipcMain.on('app:renderer-initialization-failed', handleRendererInitializationFailed);
}
return {
documentLoadStarted: startDocument,
documentLoaded: finishDocument,
rendererCrashed(details) {
if (disposed) return false;
return coordinator.rendererCrashed(details);
},
rendererInitializationFailed: handleRendererInitializationFailed,
rendererReady: handleRendererReady,
dispose() {
if (disposed) return false;
disposed = true;
if (webContents && typeof webContents.removeListener === 'function') {
webContents.removeListener('did-start-navigation', handleNavigation);
webContents.removeListener('did-finish-load', finishDocument);
webContents.removeListener('render-process-gone', handleRendererCrash);
}
if (ipcMain && typeof ipcMain.removeListener === 'function') {
ipcMain.removeListener('app:close-handshake-ready', handleRendererReady);
ipcMain.removeListener('app:renderer-initialization-failed', handleRendererInitializationFailed);
}
return coordinator.dispose();
}
};
}
function createStartupWindow(BrowserWindow, options) {
const window = new BrowserWindow({ ...options, show: false });
return {
window,
load(target, onLoadError, options) {
return window.loadFile(target, options).catch(onLoadError);
}
};
}
module.exports = {
configureStartupRenderer,
createStartupCloseHandler,
createStartupExternalRevealBindings,
createStartupFailureDocument,
createStartupNavigationLoader,
createStartupRecoveryCoordinator,
createStartupRevealGate,
createStartupRendererHandlers,
createStartupWindow,
resolveStartupLanguage
};