diff --git a/default_app/default_app.ts b/default_app/default_app.ts index b5ea5757a5f5..a7924cbd569e 100644 --- a/default_app/default_app.ts +++ b/default_app/default_app.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, BrowserWindowConstructorOptions } from 'electron' +import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron' import * as path from 'path' let mainWindow: BrowserWindow | null = null @@ -8,18 +8,52 @@ app.on('window-all-closed', () => { app.quit() }) -export const load = async (appUrl: string) => { +function decorateURL (url: string) { + // safely add `?utm_source=default_app + const parsedUrl = new URL(url) + parsedUrl.searchParams.append('utm_source', 'default_app') + return parsedUrl.toString() +} + +// Find the shortest path to the electron binary +const absoluteElectronPath = process.execPath +const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath) +const electronPath = absoluteElectronPath.length < relativeElectronPath.length + ? absoluteElectronPath + : relativeElectronPath + +const indexPath = path.resolve(app.getAppPath(), 'index.html') + +function isTrustedSender (webContents: Electron.WebContents) { + if (webContents !== (mainWindow && mainWindow.webContents)) { + return false + } + + const parsedUrl = new URL(webContents.getURL()) + return parsedUrl.protocol === 'file:' && parsedUrl.pathname === indexPath +} + +ipcMain.on('bootstrap', (event) => { + try { + event.returnValue = isTrustedSender(event.sender) ? electronPath : null + } catch { + event.returnValue = null + } +}) + +async function createWindow () { await app.whenReady() - const options: BrowserWindowConstructorOptions = { + const options: Electron.BrowserWindowConstructorOptions = { width: 900, height: 600, autoHideMenuBar: true, backgroundColor: '#FFFFFF', webPreferences: { + preload: path.resolve(__dirname, 'preload.js'), contextIsolation: true, - preload: path.resolve(__dirname, 'renderer.js'), - webviewTag: false + sandbox: true, + enableRemoteModule: false }, useContentSize: true, show: false @@ -30,9 +64,39 @@ export const load = async (appUrl: string) => { } mainWindow = new BrowserWindow(options) - mainWindow.on('ready-to-show', () => mainWindow!.show()) + mainWindow.webContents.on('new-window', (event, url) => { + event.preventDefault() + shell.openExternal(decorateURL(url)) + }) + + mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => { + const parsedUrl = new URL(webContents.getURL()) + + const options: Electron.MessageBoxOptions = { + title: 'Permission Request', + message: `Allow '${parsedUrl.origin}' to access '${permission}'?`, + buttons: ['OK', 'Cancel'], + cancelId: 1 + } + + dialog.showMessageBox(mainWindow!, options, (response) => { + done(response === 0) + }) + }) + + return mainWindow +} + +export const loadURL = async (appUrl: string) => { + mainWindow = await createWindow() mainWindow.loadURL(appUrl) mainWindow.focus() } + +export const loadFile = async (appPath: string) => { + mainWindow = await createWindow() + mainWindow.loadFile(appPath) + mainWindow.focus() +} diff --git a/default_app/index.html b/default_app/index.html index cba87e5eef31..68edcf818061 100644 --- a/default_app/index.html +++ b/default_app/index.html @@ -2,9 +2,10 @@ Electron - + + @@ -52,31 +53,31 @@