@@ -52,6 +52,10 @@ export function getWindow(name: string): Electron.BrowserWindow | null {
5252 return ( name in windowMapping ) ? BrowserWindow . fromId ( windowMapping [ name ] ) : null ;
5353}
5454
55+ function isInternalURL ( url : string ) {
56+ return url . startsWith ( `${ webRoot } /` ) || url . startsWith ( 'x-rd-extension://' ) ;
57+ }
58+
5559/**
5660 * Open a given window; if it is already open, focus it.
5761 * @param name The window identifier; this controls window re-use.
@@ -65,10 +69,6 @@ export function createWindow(name: string, url: string, options: Electron.Browse
6569 return window ;
6670 }
6771
68- const isInternalURL = ( url : string ) => {
69- return url . startsWith ( `${ webRoot } /` ) || url . startsWith ( 'x-rd-extension://' ) ;
70- } ;
71-
7272 window = new BrowserWindow ( options ) ;
7373 window . webContents . on ( 'console-message' , ( event , level , message , line , sourceId ) => {
7474 const levelNum = level === 0 ? 0 : level === 1 ? 1 : level === 2 ? 2 : level === 3 ? 3 : 0 ;
@@ -197,7 +197,7 @@ let lastOpenExtension: { id: string, relPath: string } | undefined;
197197/**
198198 * Attaches a browser view to the main window
199199 */
200- const createView = ( ) => {
200+ function createView ( ) {
201201 const mainWindow = getWindow ( 'main' ) ;
202202 const hostInfo = {
203203 arch : process . arch ,
@@ -208,15 +208,71 @@ const createView = () => {
208208 throw new Error ( 'Failed to get main window, cannot create view' ) ;
209209 }
210210
211- view = new WebContentsView ( {
212- webPreferences : {
213- nodeIntegration : false ,
214- contextIsolation : true ,
215- preload : path . join ( paths . resources , 'preload.js' ) ,
216- sandbox : true ,
217- additionalArguments : [ JSON . stringify ( hostInfo ) ] ,
218- } ,
219- } ) ;
211+ const webPreferences : Electron . WebPreferences = {
212+ nodeIntegration : false ,
213+ contextIsolation : true ,
214+ preload : path . join ( paths . resources , 'preload.js' ) ,
215+ sandbox : true ,
216+ additionalArguments : [ JSON . stringify ( hostInfo ) ] ,
217+ } ;
218+
219+ if ( currentExtension ?. id ) {
220+ webPreferences . partition = `persist:rdx-${ currentExtension . id } ` ;
221+ const webRequest = Electron . session . fromPartition ( webPreferences . partition ) . webRequest ;
222+
223+ webRequest . onBeforeSendHeaders ( ( details , callback ) => {
224+ const source = details . webContents ?. getURL ( ) ?? '' ;
225+ const requestHeaders = { ...details . requestHeaders } ;
226+
227+ if ( isInternalURL ( source ) ) {
228+ // If the request is coming from the extension, remove the Origin: header
229+ // because it has x-rd-extension:// nonsense (relative to the server).
230+ delete requestHeaders . Origin ;
231+ }
232+ callback ( { requestHeaders } ) ;
233+ } ) ;
234+
235+ webRequest . onHeadersReceived ( ( details , callback ) => {
236+ const sourceURL = details . webContents ?. getURL ( ) ?? '' ;
237+
238+ if ( ! isInternalURL ( sourceURL ) ) {
239+ // Do not rewrite requests from outside the extension (e.g. iframe).
240+ callback ( { } ) ;
241+
242+ return ;
243+ }
244+
245+ // Insert (or overwrite) CORS headers to pretend this was allowed. While
246+ // ideally we can just disable `webSecurity` instead, that seems to break
247+ // the preload script (which breaks the extension APIs).
248+ const responseHeaders : Record < string , string | string [ ] > = { ...details . responseHeaders } ;
249+ // HTTP headers use case-insensitive comparison; but accents should count
250+ // as different characters (even though it should be ASCII only).
251+ const { compare } = new Intl . Collator ( 'en' , { sensitivity : 'accent' } ) ;
252+ const overwriteHeaders = [
253+ 'Access-Control-Allow-Headers' ,
254+ 'Access-Control-Allow-Methods' ,
255+ 'Access-Control-Allow-Origin' ,
256+ ] ;
257+
258+ for ( const header of overwriteHeaders ) {
259+ const match = Object . keys ( responseHeaders ) . find ( k => compare ( header , k ) === 0 ) ;
260+
261+ responseHeaders [ match ?? header ] = '*' ;
262+ }
263+
264+ if ( details . method !== 'OPTIONS' ) {
265+ // For any request that's not a CORS preflight, just overwrite the headers.
266+ callback ( { responseHeaders } ) ;
267+ } else {
268+ // For CORS preflights, also change the status code.
269+ const prefix = / \s + / . exec ( details . statusLine ) ?. shift ( ) ?? 'HTTP/1.1' ;
270+
271+ callback ( { responseHeaders, statusLine : `${ prefix } 204 No Content` } ) ;
272+ }
273+ } ) ;
274+ }
275+ view = new WebContentsView ( { webPreferences } ) ;
220276 mainWindow . contentView . addChildView ( view ) ;
221277 mainWindow . contentView . addListener ( 'bounds-changed' , ( ) => {
222278 setImmediate ( ( ) => mainWindow . webContents . send ( 'extensions/getContentArea' ) ) ;
@@ -225,7 +281,7 @@ const createView = () => {
225281 const backgroundColor = nativeTheme . shouldUseDarkColors ? '#202c33' : '#f4f4f6' ;
226282
227283 view . setBackgroundColor ( backgroundColor ) ;
228- } ;
284+ }
229285
230286/**
231287 * Updates the browser view size and position
0 commit comments