Skip to content

Commit 3526525

Browse files
change electron's Content-Security-Policy
1 parent 2f151e3 commit 3526525

File tree

5 files changed

+195
-60
lines changed

5 files changed

+195
-60
lines changed

apps/studio/electron/main/index.ts

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APP_NAME, APP_SCHEMA } from '@onlook/models/constants';
2-
import { BrowserWindow, app, shell, protocol } from 'electron';
2+
import { BrowserWindow, app, shell, protocol, session } from 'electron';
33
import fixPath from 'fix-path';
44
import { createRequire } from 'node:module';
55
import os from 'node:os';
@@ -67,10 +67,28 @@ const createWindow = () => {
6767
titleBarStyle: 'hiddenInset',
6868
frame: false,
6969
webPreferences: {
70+
webSecurity: false, // Disable for development
71+
allowRunningInsecureContent: true,
72+
nodeIntegration: false,
73+
contextIsolation: true,
74+
sandbox: true,
7075
preload: PRELOAD_PATH,
71-
webviewTag: true,
7276
},
7377
});
78+
79+
// Set parent window CSP
80+
mainWindow.webContents.on('did-finish-load', () => {
81+
mainWindow?.webContents.insertCSS(`
82+
meta http-equiv="Content-Security-Policy"
83+
content="default-src 'self' onlook:;
84+
script-src 'self' 'unsafe-inline' 'unsafe-eval' onlook:;
85+
script-src-elem 'self' onlook:;
86+
style-src 'self' 'unsafe-inline';
87+
img-src 'self' data:;
88+
connect-src *;"
89+
`);
90+
});
91+
7492
return mainWindow;
7593
};
7694

@@ -80,6 +98,33 @@ const loadWindowContent = (win: BrowserWindow) => {
8098

8199
const initMainWindow = () => {
82100
const win = createWindow();
101+
102+
// Enhanced webRequest handling
103+
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
104+
// console.log(`Intercepted headers for: ${details.url}`);
105+
106+
// Match webview-preload.js requests from any origin
107+
if (details.url.endsWith('/webview-preload.js')) {
108+
console.log('Modifying headers for preload script');
109+
callback({
110+
responseHeaders: {
111+
...details.responseHeaders,
112+
'Access-Control-Allow-Origin': ['*'],
113+
'Access-Control-Allow-Headers': ['*'],
114+
'Content-Security-Policy': [
115+
"default-src 'self' 'unsafe-inline' data: onlook:; " +
116+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' onlook:; " +
117+
"script-src-elem 'self' onlook:; " +
118+
'connect-src *',
119+
],
120+
},
121+
});
122+
} else {
123+
callback({ cancel: false });
124+
}
125+
});
126+
127+
// Rest of existing init code...
83128
win.maximize();
84129
loadWindowContent(win);
85130
win.webContents.setWindowOpenHandler(({ url }) => {
@@ -141,14 +186,51 @@ const listenForExitEvents = () => {
141186
});
142187
};
143188

189+
const checkPreloadFile = () => {
190+
const preloadPath = path.join(__dirname, '../preload/webview.js');
191+
console.log('Preload file exists:', fs.existsSync(preloadPath));
192+
console.log('Preload file path:', preloadPath);
193+
};
194+
195+
const configureSecurity = () => {
196+
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
197+
callback({
198+
responseHeaders: {
199+
...details.responseHeaders,
200+
'Access-Control-Allow-Origin': ['*'],
201+
'Access-Control-Allow-Methods': ['*'],
202+
'Access-Control-Allow-Headers': ['*'],
203+
},
204+
});
205+
});
206+
};
207+
144208
const setupAppEventListeners = () => {
145209
app.whenReady().then(() => {
210+
// 1. First configure security headers
211+
configureSecurity();
212+
213+
// 2. Then register protocol handler
146214
protocol.handle('onlook', (request) => {
147215
const filePath = path.join(__dirname, '../preload/webview.js');
148-
return new Response(fs.readFileSync(filePath));
216+
return new Response(fs.readFileSync(filePath), {
217+
headers: {
218+
'Content-Type': 'application/javascript',
219+
'Content-Security-Policy':
220+
"script-src script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' onlook:",
221+
'Access-Control-Allow-Origin': '*',
222+
},
223+
});
149224
});
225+
226+
// 3. Then initialize main window
227+
checkPreloadFile();
150228
listenForExitEvents();
151229
initMainWindow();
230+
231+
// 4. Then other setup
232+
updater.listen();
233+
sendAnalytics('start app');
152234
});
153235

154236
app.on('ready', () => {
@@ -195,28 +277,34 @@ const main = () => {
195277
setupEnvironment();
196278
configurePlatformSpecifics();
197279

198-
// Register onlook protocol handler for browser-compatible preload script urls
280+
// MUST BE CALLED BEFORE APP.READY
199281
protocol.registerSchemesAsPrivileged([
200282
{
201283
scheme: 'onlook',
202284
privileges: {
203285
standard: true,
204286
secure: true,
205-
allowServiceWorkers: true,
206287
supportFetchAPI: true,
288+
corsEnabled: true,
207289
stream: true,
208290
},
209291
},
210292
]);
211293

212-
if (!app.requestSingleInstanceLock()) {
213-
app.quit();
214-
process.exit(0);
215-
}
294+
app.whenReady()
295+
.then(() => {
296+
configureSecurity(); // Now only handles CORS headers
297+
setupProtocol();
298+
setupAppEventListeners();
299+
listenForIpcMessages();
300+
})
301+
.catch(console.error);
216302

217-
setupProtocol();
218-
setupAppEventListeners();
219-
listenForIpcMessages();
303+
app.commandLine.appendSwitch('disable-web-security');
304+
app.commandLine.appendSwitch('ignore-certificate-errors');
305+
app.commandLine.appendSwitch('allow-running-insecure-content');
306+
app.commandLine.appendSwitch('disable-site-isolation-trials');
307+
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors');
220308
};
221309

222310
main();

apps/studio/electron/preload/webview/index.ts

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,50 @@ import { processDom } from './dom';
44
import { listenForEvents } from './events';
55
import cssManager from './style';
66

7-
function handleBodyReady() {
8-
setApi();
9-
listenForEvents();
10-
keepDomUpdated();
11-
cssManager.injectDefaultStyles();
7+
// Initialize API first
8+
setApi();
9+
10+
// Wait for document to be ready before initializing features
11+
function initializeFeatures() {
12+
try {
13+
// Initialize core features
14+
cssManager.injectDefaultStyles();
15+
listenForEvents();
16+
17+
// Set up message handler after core initialization
18+
(window as TOnlookWindow).onlook.bridge.receive((event) => {
19+
if (event.data.type === WebviewChannels.EXECUTE_CODE) {
20+
const { code, messageId } = event.data;
21+
const [port] = event.ports;
22+
23+
try {
24+
const result = eval(code);
25+
port.postMessage({ result, messageId });
26+
} catch (error) {
27+
port.postMessage({
28+
error: error instanceof Error ? error.message : 'Unknown error',
29+
messageId,
30+
});
31+
}
32+
}
33+
});
34+
35+
// Start DOM processing
36+
processDom();
37+
} catch (err) {
38+
console.error('Error initializing features:', err);
39+
}
40+
}
41+
42+
// Use DOMContentLoaded instead of checking body
43+
if (document.readyState === 'loading') {
44+
document.addEventListener('DOMContentLoaded', initializeFeatures);
45+
} else {
46+
initializeFeatures();
1247
}
1348

49+
console.debug('Webview preload script loaded');
50+
1451
let domUpdateInterval: ReturnType<typeof setInterval> | null = null;
1552

1653
function keepDomUpdated() {
@@ -35,34 +72,24 @@ function keepDomUpdated() {
3572
domUpdateInterval = interval;
3673
}
3774

38-
const handleDocumentBody = setInterval(() => {
75+
const handleDocumentBody = (interval: ReturnType<typeof setInterval> | null) => {
3976
window.onerror = function logError(errorMsg, url, lineNumber) {
4077
console.log(`Unhandled error: ${errorMsg} ${url} ${lineNumber}`);
4178
};
4279

4380
if (window?.document?.body) {
44-
clearInterval(handleDocumentBody);
81+
if (interval !== null) {
82+
clearInterval(interval);
83+
}
4584
try {
46-
handleBodyReady();
85+
keepDomUpdated();
4786
} catch (err) {
4887
console.log('Error in documentBodyInit:', err);
4988
}
5089
}
51-
}, 300);
90+
};
5291

53-
(window as TOnlookWindow).onlook.bridge.receive((event) => {
54-
if (event.data.type === WebviewChannels.EXECUTE_CODE) {
55-
const { code, messageId } = event.data;
56-
const [port] = event.ports;
92+
// TODO: Reduce this back to 300ms
93+
// const interval = setInterval(() => handleDocumentBody(interval), 2000);
5794

58-
try {
59-
const result = eval(code);
60-
port.postMessage({ result, messageId });
61-
} catch (error) {
62-
port.postMessage({
63-
error: error instanceof Error ? error.message : 'Unknown error',
64-
messageId,
65-
});
66-
}
67-
}
68-
});
95+
handleDocumentBody(null);

apps/studio/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<meta
88
http-equiv="Content-Security-Policy"
9-
content="script-src 'self' 'unsafe-inline' 'unsafe-eval';"
9+
content="script-src script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' onlook:;"
1010
/>
1111
<title>Onlook</title>
1212
</head>

apps/studio/src/lib/editor/engine/frameview.tsx

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
} from 'react';
88
import type { NativeImage } from 'electron';
99
import { WebviewChannels } from '@onlook/models/constants';
10-
import type { TOnlookWindow } from '/electron/preload/webview/api';
1110

1211
export type IFrameView = HTMLIFrameElement &
1312
Pick<
@@ -37,32 +36,44 @@ export const FrameView = forwardRef<IFrameView, IFrameViewProps>(({ preload, ...
3736
const zoomLevel = useRef(1);
3837

3938
const handleIframeLoad = useCallback(() => {
39+
console.log('FrameView handleIframeLoad triggered');
4040
const iframe = iframeRef.current;
4141
if (!iframe || !preload) {
42-
console.error('No iframe or preload');
42+
console.error('Missing iframe or preload path:', { iframe, preload });
4343
return;
4444
}
4545

46-
const injectPreloadScript = () => {
47-
try {
48-
const doc = iframe.contentDocument;
49-
if (!doc) {
50-
console.error('No document found');
51-
return;
46+
console.log('Attempting to add content load listener');
47+
48+
try {
49+
// Safe cross-origin event listener
50+
iframe.addEventListener('load', () => {
51+
console.log('Iframe surface load event fired');
52+
try {
53+
if (!iframe.contentWindow?.document) {
54+
console.error('No contentDocument available');
55+
return;
56+
}
57+
58+
console.log('Injecting preload script');
59+
const script = iframe.contentWindow?.document.createElement('script');
60+
if (script) {
61+
script.src = preload!;
62+
console.log('set script src:', script.src);
63+
64+
script.onerror = (e) => console.error('Preload script error:', e);
65+
script.onload = () => console.log('Preload script loaded');
66+
67+
iframe.contentWindow?.document.head.appendChild(script);
68+
} else {
69+
console.error('Failed to create script element');
70+
}
71+
} catch (error) {
72+
console.error('Injection error:', error);
5273
}
53-
54-
const script = doc.createElement('script');
55-
script.src = preload;
56-
doc.head.appendChild(script);
57-
} catch (error) {
58-
console.error('Preload injection failed:', error);
59-
}
60-
};
61-
62-
if (iframe.contentDocument?.readyState === 'complete') {
63-
injectPreloadScript();
64-
} else {
65-
iframe.addEventListener('load', injectPreloadScript, { once: true });
74+
});
75+
} catch (error) {
76+
console.error('Failed to add load listener:', error);
6677
}
6778
}, [preload]);
6879

@@ -209,7 +220,16 @@ export const FrameView = forwardRef<IFrameView, IFrameViewProps>(({ preload, ...
209220
return iframe as IFrameView;
210221
}, []);
211222

212-
return <iframe ref={iframeRef} {...props} onLoad={handleIframeLoad} />;
223+
return (
224+
<iframe
225+
ref={iframeRef}
226+
{...props}
227+
onLoad={(e) => {
228+
console.log('Native iframe load event fired');
229+
handleIframeLoad();
230+
}}
231+
/>
232+
);
213233
});
214234

215235
FrameView.displayName = 'FrameView';

apps/studio/src/routes/editor/WebviewArea/Frame.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ const Frame = observer(
343343
)}
344344
src={settings.url}
345345
preload="onlook://webview-preload.js"
346-
sandbox="allow-popups allow-scripts allow-same-origin"
346+
sandbox="allow-popups allow-scripts allow-same-origin allow-forms"
347347
style={{
348348
width: clampedDimensions.width,
349349
height: clampedDimensions.height,

0 commit comments

Comments
 (0)