Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(draft): replace <webview> with <iframe> #1191

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

theshadowagent
Copy link

@theshadowagent theshadowagent commented Jan 31, 2025

Description

What is the purpose of this pull request?

  • New feature
  • Documentation update
  • Bug fix
  • Refactor
  • Release
  • Other

@theshadowagent theshadowagent marked this pull request as draft January 31, 2025 23:00
@theshadowagent theshadowagent changed the title refactor: replace <webview> with <iframe> refactor(draft): replace <webview> with <iframe> Jan 31, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR replaces Electron's elements with standard <iframe> elements, introducing a custom message bridge and IFrameView implementation to maintain existing functionality while moving away from Electron-specific APIs.

  • Added new FrameView component in apps/studio/src/lib/editor/engine/frameview.tsx implementing webview-like functionality through iframes with potential security concerns around postMessage origin
  • Implemented custom message bridge in apps/studio/electron/preload/webview/api.ts using window.postMessage instead of Electron's IPC
  • Added onlook:// protocol handler in apps/studio/electron/main/index.ts for secure preload script delivery to iframes
  • Concerning use of eval() for code execution in apps/studio/electron/preload/webview/index.ts poses security risks
  • Type safety issues in apps/studio/src/lib/editor/messageBridge.ts with 'as any' casting for event handlers

💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!

24 file(s) reviewed, 20 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +49 to 52
(window as TOnlookWindow).onlook.bridge.send(WebviewChannels.WINDOW_MUTATED, {
added: Object.fromEntries(added),
removed: Object.fromEntries(removed),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Type assertion to TOnlookWindow could fail silently if window.onlook or bridge is undefined. Consider adding a null check or error handling.

api: onlookApi,
bridge: {
send: (channel: string, data?: any, transfer?: Transferable[]) => {
window.parent.postMessage({ type: channel, data }, '*', transfer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Using '*' as targetOrigin in postMessage is a security risk. Should restrict to specific origins.

Comment on lines +81 to +83
window.addEventListener('message', (event) => {
handler(event, event.data);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing origin validation in message event listener. Should verify event.origin matches expected domains.

Suggested change
window.addEventListener('message', (event) => {
handler(event, event.data);
});
window.addEventListener('message', (event) => {
if (event.origin === window.location.origin) {
handler(event, event.data);
}
});

Comment on lines +74 to +86
(window as TOnlookWindow).onlook = {
api: onlookApi,
bridge: {
send: (channel: string, data?: any, transfer?: Transferable[]) => {
window.parent.postMessage({ type: channel, data }, '*', transfer);
},
receive: (handler: (event: MessageEvent, data?: any) => void) => {
window.addEventListener('message', (event) => {
handler(event, event.data);
});
},
},
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Direct window object modification without Object.defineProperty may allow overwriting of onlook API. Consider using Object.defineProperty with configurable: false.

Comment on lines +146 to +149
protocol.handle('onlook', (request) => {
const filePath = path.join(__dirname, '../preload/webview.js');
return new Response(fs.readFileSync(filePath));
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Reading file synchronously could block the main process. Consider using async fs.promises.readFile instead

Suggested change
protocol.handle('onlook', (request) => {
const filePath = path.join(__dirname, '../preload/webview.js');
return new Response(fs.readFileSync(filePath));
});
protocol.handle('onlook', async (request) => {
const filePath = path.join(__dirname, '../preload/webview.js');
const content = await fs.promises.readFile(filePath);
return new Response(content);
});

Comment on lines 31 to 33
return async (e: Electron.IpcMessageEvent) => {
const webview = e.target as Electron.WebviewTag;
const webview = e.target as IFrameView;
if (!e.args || e.args.length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: IpcMessageEvent may not be the correct event type for iframes. Need to verify event type compatibility between Electron.WebviewTag and IFrameView.

Comment on lines 207 to 208
await webview.executeJavaScript(`window.api.setWebviewId('${webview.id}')`);
await webview.executeJavaScript(`window.api.processDom()`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: executeJavaScript method may not be available or work the same way in IFrameView as it did in WebviewTag. Need to ensure this API is properly implemented in the new IFrameView interface.

@@ -17,7 +18,7 @@ export class WebviewMessageBridge {
};
}

register(webview: Electron.WebviewTag, id: string) {
register(webview: IFrameView, id: string) {
const handlerRemovers: (() => void)[] = [];
Object.entries(this.eventHandlers).forEach(([event, handler]) => {
webview.addEventListener(event, handler as any);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using 'as any' type assertion masks potential incompatibilities between iframe and webview event systems. Consider creating proper type definitions.

Comment on lines +39 to 44
export const sendToWebview = <T>(webview: IFrameView, channel: WebviewChannels, ...args: T[]) => {
if (!webview.contentWindow) {
throw new Error('Could not call sendToWebview(): .contentWindow is null/undefined');
}
return webview.contentWindow.postMessage({ channel, data: args.map(jsonClone) }, '*');
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The postMessage API requires proper origin validation. Consider adding a check for allowed origins before sending messages to prevent potential security vulnerabilities.

Comment on lines 346 to 347
sandbox="allow-popups allow-scripts allow-same-origin"
style={{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: sandbox permissions may be too permissive - consider if 'allow-same-origin' is necessary and review security implications

@theshadowagent theshadowagent force-pushed the refactor/webview-to-iframe branch from 3526525 to 48fea5a Compare January 31, 2025 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant