| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Create and minimally track guest windows at the direction of the renderer | 
					
						
							|  |  |  |  * (via window.open). Here, "guest" roughly means "child" — it's not necessarily | 
					
						
							| 
									
										
										
										
											2022-01-06 09:28:03 -08:00
										 |  |  |  * emblematic of its process status; both in-process (same-origin) and | 
					
						
							|  |  |  |  * out-of-process (cross-origin) are created here. "Embedder" roughly means | 
					
						
							|  |  |  |  * "parent." | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-08-09 23:57:05 +02:00
										 |  |  | import { BrowserWindow } from 'electron/main'; | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main'; | 
					
						
							| 
									
										
										
										
											2021-11-10 17:54:51 +01:00
										 |  |  | import { parseFeatures } from '@electron/internal/browser/parse-features-string'; | 
					
						
							| 
									
										
										
										
											2016-03-18 11:51:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | type PostData = LoadURLOptions['postData'] | 
					
						
							|  |  |  | export type WindowOpenArgs = { | 
					
						
							|  |  |  |   url: string, | 
					
						
							|  |  |  |   frameName: string, | 
					
						
							|  |  |  |   features: string, | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-13 00:28:34 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | const frameNamesToWindow = new Map<string, BrowserWindow>(); | 
					
						
							|  |  |  | const registerFrameNameToGuestWindow = (name: string, win: BrowserWindow) => frameNamesToWindow.set(name, win); | 
					
						
							|  |  |  | const unregisterFrameName = (name: string) => frameNamesToWindow.delete(name); | 
					
						
							|  |  |  | const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2022-01-06 09:28:03 -08:00
										 |  |  |  * `openGuestWindow` is called to create and setup event handling for the new | 
					
						
							|  |  |  |  * window. | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-08-09 23:57:05 +02:00
										 |  |  | export function openGuestWindow ({ embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: { | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   embedder: WebContents, | 
					
						
							|  |  |  |   guest?: WebContents, | 
					
						
							|  |  |  |   referrer: Referrer, | 
					
						
							|  |  |  |   disposition: string, | 
					
						
							|  |  |  |   postData?: PostData, | 
					
						
							|  |  |  |   overrideBrowserWindowOptions?: BrowserWindowConstructorOptions, | 
					
						
							|  |  |  |   windowOpenArgs: WindowOpenArgs, | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  |   outlivesOpener: boolean, | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | }): BrowserWindow | undefined { | 
					
						
							|  |  |  |   const { url, frameName, features } = windowOpenArgs; | 
					
						
							| 
									
										
										
										
											2022-08-09 23:57:05 +02:00
										 |  |  |   const { options: parsedOptions } = parseFeatures(features); | 
					
						
							|  |  |  |   const browserWindowOptions = { | 
					
						
							|  |  |  |     show: true, | 
					
						
							|  |  |  |     width: 800, | 
					
						
							|  |  |  |     height: 600, | 
					
						
							|  |  |  |     ...parsedOptions, | 
					
						
							|  |  |  |     ...overrideBrowserWindowOptions | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // To spec, subsequent window.open calls with the same frame name (`target` in
 | 
					
						
							|  |  |  |   // spec parlance) will reuse the previous window.
 | 
					
						
							|  |  |  |   // https://html.spec.whatwg.org/multipage/window-object.html#apis-for-creating-and-navigating-browsing-contexts-by-name
 | 
					
						
							|  |  |  |   const existingWindow = getGuestWindowByFrameName(frameName); | 
					
						
							|  |  |  |   if (existingWindow) { | 
					
						
							| 
									
										
										
										
											2021-10-19 02:57:10 +02:00
										 |  |  |     if (existingWindow.isDestroyed() || existingWindow.webContents.isDestroyed()) { | 
					
						
							|  |  |  |       // FIXME(t57ser): The webContents is destroyed for some reason, unregister the frame name
 | 
					
						
							|  |  |  |       unregisterFrameName(frameName); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       existingWindow.loadURL(url); | 
					
						
							|  |  |  |       return existingWindow; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-03-30 10:51:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   const window = new BrowserWindow({ | 
					
						
							|  |  |  |     webContents: guest, | 
					
						
							|  |  |  |     ...browserWindowOptions | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-04-21 10:59:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 20:34:33 +02:00
										 |  |  |   if (!guest) { | 
					
						
							|  |  |  |     // When we open a new window from a link (via OpenURLFromTab),
 | 
					
						
							|  |  |  |     // the browser process is responsible for initiating navigation
 | 
					
						
							|  |  |  |     // in the new window.
 | 
					
						
							|  |  |  |     window.loadURL(url, { | 
					
						
							|  |  |  |       httpReferrer: referrer, | 
					
						
							|  |  |  |       ...(postData && { | 
					
						
							|  |  |  |         postData, | 
					
						
							|  |  |  |         extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[]) | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  |   handleWindowLifecycleEvents({ embedder, frameName, guest: window, outlivesOpener }); | 
					
						
							| 
									
										
										
										
											2016-09-29 15:43:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 15:46:54 -07:00
										 |  |  |   embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData }); | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   return window; | 
					
						
							| 
									
										
										
										
											2020-03-26 19:05:45 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Manage the relationship between embedder window and guest window. When the | 
					
						
							|  |  |  |  * guest is destroyed, notify the embedder. When the embedder is destroyed, so | 
					
						
							|  |  |  |  * too is the guest destroyed; this is Electron convention and isn't based in | 
					
						
							|  |  |  |  * browser behavior. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  | const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outlivesOpener }: { | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   embedder: WebContents, | 
					
						
							|  |  |  |   guest: BrowserWindow, | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  |   frameName: string, | 
					
						
							|  |  |  |   outlivesOpener: boolean | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | }) { | 
					
						
							| 
									
										
										
										
											2016-06-09 10:35:48 -07:00
										 |  |  |   const closedByEmbedder = function () { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     guest.removeListener('closed', closedByUser); | 
					
						
							|  |  |  |     guest.destroy(); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 10:35:48 -07:00
										 |  |  |   const closedByUser = function () { | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  |     // Embedder might have been closed
 | 
					
						
							|  |  |  |     if (!embedder.isDestroyed() && !outlivesOpener) { | 
					
						
							|  |  |  |       embedder.removeListener('current-render-view-deleted' as any, closedByEmbedder); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2022-02-23 08:59:50 +01:00
										 |  |  |   if (!outlivesOpener) { | 
					
						
							|  |  |  |     embedder.once('current-render-view-deleted' as any, closedByEmbedder); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   guest.once('closed', closedByUser); | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  |   if (frameName) { | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |     registerFrameNameToGuestWindow(frameName, guest); | 
					
						
							| 
									
										
										
										
											2016-03-24 13:15:04 -07:00
										 |  |  |     guest.once('closed', function () { | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |       unregisterFrameName(frameName); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  | // Security options that child windows will always inherit from parent windows
 | 
					
						
							|  |  |  | const securityWebPreferences: { [key: string]: boolean } = { | 
					
						
							|  |  |  |   contextIsolation: true, | 
					
						
							|  |  |  |   javascript: false, | 
					
						
							|  |  |  |   nodeIntegration: false, | 
					
						
							|  |  |  |   sandbox: true, | 
					
						
							|  |  |  |   webviewTag: false, | 
					
						
							|  |  |  |   nodeIntegrationInSubFrames: false, | 
					
						
							|  |  |  |   enableWebSQL: false | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-01-11 18:40:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-21 10:55:17 -07:00
										 |  |  | export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {}, insecureParsedWebPreferences: parsedWebPreferences = {} }: { | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   embedder: WebContents, | 
					
						
							|  |  |  |   insecureParsedWebPreferences?: ReturnType<typeof parseFeatures>['webPreferences'], | 
					
						
							|  |  |  |   // Note that override preferences are considered elevated, and should only be
 | 
					
						
							|  |  |  |   // sourced from the main process, as they override security defaults. If you
 | 
					
						
							|  |  |  |   // have unvetted prefs, use parsedWebPreferences.
 | 
					
						
							|  |  |  |   secureOverrideWebPreferences?: BrowserWindowConstructorOptions['webPreferences'], | 
					
						
							|  |  |  | }) { | 
					
						
							| 
									
										
										
										
											2021-07-27 07:48:12 +02:00
										 |  |  |   const parentWebPreferences = embedder.getLastWebPreferences()!; | 
					
						
							| 
									
										
										
										
											2021-01-29 21:41:59 +01:00
										 |  |  |   const securityWebPreferencesFromParent = (Object.keys(securityWebPreferences).reduce((map, key) => { | 
					
						
							|  |  |  |     if (securityWebPreferences[key] === parentWebPreferences[key as keyof Electron.WebPreferences]) { | 
					
						
							|  |  |  |       (map as any)[key] = parentWebPreferences[key as keyof Electron.WebPreferences]; | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |     return map; | 
					
						
							| 
									
										
										
										
											2021-01-29 21:41:59 +01:00
										 |  |  |   }, {} as Electron.WebPreferences)); | 
					
						
							| 
									
										
										
										
											2018-12-04 16:12:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     ...parsedWebPreferences, | 
					
						
							|  |  |  |     // Note that order is key here, we want to disallow the renderer's
 | 
					
						
							|  |  |  |     // ability to change important security options but allow main (via
 | 
					
						
							|  |  |  |     // setWindowOpenHandler) to change them.
 | 
					
						
							|  |  |  |     ...securityWebPreferencesFromParent, | 
					
						
							| 
									
										
										
										
											2022-01-06 09:28:03 -08:00
										 |  |  |     ...secureOverrideWebPreferences | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-24 00:45:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 20:34:33 +02:00
										 |  |  | function formatPostDataHeaders (postData: PostData) { | 
					
						
							|  |  |  |   if (!postData) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { contentType, boundary } = parseContentTypeFormat(postData); | 
					
						
							|  |  |  |   if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return `content-type: ${contentType}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-06 09:54:05 -07:00
										 |  |  | const MULTIPART_CONTENT_TYPE = 'multipart/form-data'; | 
					
						
							|  |  |  | const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Figure out appropriate headers for post data.
 | 
					
						
							| 
									
										
										
										
											2021-04-13 12:35:27 -07:00
										 |  |  | export const parseContentTypeFormat = function (postData: Exclude<PostData, undefined>) { | 
					
						
							| 
									
										
										
										
											2021-04-06 09:54:05 -07:00
										 |  |  |   if (postData.length) { | 
					
						
							|  |  |  |     if (postData[0].type === 'rawData') { | 
					
						
							|  |  |  |       // For multipart forms, the first element will start with the boundary
 | 
					
						
							|  |  |  |       // notice, which looks something like `------WebKitFormBoundary12345678`
 | 
					
						
							|  |  |  |       // Note, this regex would fail when submitting a urlencoded form with an
 | 
					
						
							|  |  |  |       // input attribute of name="--theKey", but, uhh, don't do that?
 | 
					
						
							|  |  |  |       const postDataFront = postData[0].bytes.toString(); | 
					
						
							|  |  |  |       const boundary = /^--.*[^-\r\n]/.exec(postDataFront); | 
					
						
							|  |  |  |       if (boundary) { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           boundary: boundary[0].substr(2), | 
					
						
							|  |  |  |           contentType: MULTIPART_CONTENT_TYPE | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-11-10 09:06:03 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-11-25 10:03:47 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-04-06 09:54:05 -07:00
										 |  |  |   // Either the form submission didn't contain any inputs (the postData array
 | 
					
						
							|  |  |  |   // was empty), or we couldn't find the boundary and thus we can assume this is
 | 
					
						
							|  |  |  |   // a key=value style form.
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     contentType: URL_ENCODED_CONTENT_TYPE | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; |