refactor(webkit): address Copilot PR review comments
Address all 7 Copilot review comments from PR #3: 1. Remove deprecated `onPopup` field - unused, replaced by `onPopupCreate` 2. Remove unused `NewWebViewWithRelated` function - popups use `NewBareRelatedWebView` 3. Implement print functionality - `ShowPrintDialog()` now calls `webkit.NewPrintOperation()` 4. Fix WebView ID injection - use number instead of string to eliminate any theoretical injection concern 5. Use JSON marshaling for keyboard shortcuts - prevents potential script injection All changes maintain backward compatibility: - JavaScript already handles webview ID as `string | number` - JSON marshaling produces valid JavaScript string literals - Print function is bound to Ctrl+Shift+P and actively used Co-authored-by: Copilot <copilot@github.com>
- commit
- 1d94071a1ea92945d1751c87cf65ec348ad77c76
- author
- bamen · · 2025-10-15 04:19 UTC
- parent
- 19c41e7
- signature
- unsigned
3 files · +13 -26
M pkg/webkit/keyboard_bridge.go +8 -2
| @@ -1,6 +1,7 @@ | |||
| 1 | 1 | package webkit | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "encoding/json" | |
| 4 | 5 | "fmt" | |
| 5 | 6 | "log" | |
| 6 | 7 | ||
| @@ -254,8 +255,13 @@ func (w *WebView) AttachKeyboardBridge() { | |||
| 254 | 255 | return true | |
| 255 | 256 | } | |
| 256 | 257 | ||
| 257 | - | // Forward to JavaScript | |
| 258 | - | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:'%s'}}));`, shortcut) | |
| 258 | + | // Forward to JavaScript using proper JSON marshaling to prevent any potential injection | |
| 259 | + | shortcutJSON, err := json.Marshal(shortcut) | |
| 260 | + | if err != nil { | |
| 261 | + | log.Printf("[keyboard-bridge] Failed to marshal shortcut: %v", err) | |
| 262 | + | return false | |
| 263 | + | } | |
| 264 | + | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:%s}}));`, shortcutJSON) | |
| 259 | 265 | if err := w.InjectScript(script); err != nil { | |
| 260 | 266 | log.Printf("[keyboard-bridge] Failed to dispatch: %v", err) | |
| 261 | 267 | } | |
| @@ -1,6 +1,7 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "encoding/json" | |||
| 4 | "fmt" | 5 | "fmt" | ||
| 5 | "log" | 6 | "log" | ||
| 6 | 7 | ||||
| @@ -254,8 +255,13 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 254 | return true | 255 | return true | ||
| 255 | } | 256 | } | ||
| 256 | 257 | ||||
| 257 | - | // Forward to JavaScript | 258 | + | // Forward to JavaScript using proper JSON marshaling to prevent any potential injection |
| 258 | - | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:'%s'}}));`, shortcut) | 259 | + | shortcutJSON, err := json.Marshal(shortcut) |
| 260 | + | if err != nil { | |||
| 261 | + | log.Printf("[keyboard-bridge] Failed to marshal shortcut: %v", err) | |||
| 262 | + | return false | |||
| 263 | + | } | |||
| 264 | + | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:%s}}));`, shortcutJSON) | |||
| 259 | if err := w.InjectScript(script); err != nil { | 265 | if err := w.InjectScript(script); err != nil { | ||
| 260 | log.Printf("[keyboard-bridge] Failed to dispatch: %v", err) | 266 | log.Printf("[keyboard-bridge] Failed to dispatch: %v", err) | ||
| 261 | } | 267 | } | ||
M pkg/webkit/user_content.go +2 -1
| @@ -23,8 +23,9 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||
| 23 | 23 | } | |
| 24 | 24 | ||
| 25 | 25 | // Inject webview ID FIRST, so GUI scripts can access it immediately | |
| 26 | + | // Note: webviewID is uint64, formatted as number (not string) to avoid any injection concerns | |
| 26 | 27 | webviewIDScript := fmt.Sprintf(` | |
| 27 | - | window.__dumber_webview_id = "%d"; | |
| 28 | + | window.__dumber_webview_id = %d; | |
| 28 | 29 | console.log('[webkit] WebView ID set in JavaScript:', window.__dumber_webview_id); | |
| 29 | 30 | `, webviewID) | |
| 30 | 31 | ucm.AddScript(webkit.NewUserScript( | |
| @@ -23,8 +23,9 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 23 | } | 23 | } | ||
| 24 | 24 | ||||
| 25 | // Inject webview ID FIRST, so GUI scripts can access it immediately | 25 | // Inject webview ID FIRST, so GUI scripts can access it immediately | ||
| 26 | + | // Note: webviewID is uint64, formatted as number (not string) to avoid any injection concerns | |||
| 26 | webviewIDScript := fmt.Sprintf(` | 27 | webviewIDScript := fmt.Sprintf(` | ||
| 27 | - | window.__dumber_webview_id = "%d"; | 28 | + | window.__dumber_webview_id = %d; |
| 28 | console.log('[webkit] WebView ID set in JavaScript:', window.__dumber_webview_id); | 29 | console.log('[webkit] WebView ID set in JavaScript:', window.__dumber_webview_id); | ||
| 29 | `, webviewID) | 30 | `, webviewID) | ||
| 30 | ucm.AddScript(webkit.NewUserScript( | 31 | ucm.AddScript(webkit.NewUserScript( | ||
M pkg/webkit/webview.go +3 -23
| @@ -37,7 +37,6 @@ type WebView struct { | |||
| 37 | 37 | onFaviconChanged func([]byte) | |
| 38 | 38 | onFaviconURIChanged func(pageURI, faviconURI string) | |
| 39 | 39 | onZoomChanged func(float64) | |
| 40 | - | onPopup func(string) *WebView // Deprecated: Use onPopupCreate instead | |
| 41 | 40 | onPopupCreate func(*webkit.NavigationAction) *WebView // New WebKit create signal handler | |
| 42 | 41 | onReadyToShow func() // WebKit ready-to-show signal handler | |
| 43 | 42 | onClose func() | |
| @@ -158,18 +157,6 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||
| 158 | 157 | return wv, nil | |
| 159 | 158 | } | |
| 160 | 159 | ||
| 161 | - | // NewWebViewWithRelated creates a new WebView related to an existing one | |
| 162 | - | // (shares process context) | |
| 163 | - | func NewWebViewWithRelated(cfg *Config, related *WebView) (*WebView, error) { | |
| 164 | - | if related == nil { | |
| 165 | - | return NewWebView(cfg) | |
| 166 | - | } | |
| 167 | - | ||
| 168 | - | // TODO: Implement related view creation using webkit.NewWebViewWithRelatedView | |
| 169 | - | // For now, create a regular view | |
| 170 | - | return NewWebView(cfg) | |
| 171 | - | } | |
| 172 | - | ||
| 173 | 160 | // applyConfig applies the configuration to the WebView settings | |
| 174 | 161 | func (w *WebView) applyConfig() error { | |
| 175 | 162 | settings := w.view.Settings() | |
| @@ -498,13 +485,6 @@ func (w *WebView) RegisterZoomChangedHandler(handler func(float64)) { | |||
| 498 | 485 | w.onZoomChanged = handler | |
| 499 | 486 | } | |
| 500 | 487 | ||
| 501 | - | // RegisterPopupHandler registers a handler for popup requests | |
| 502 | - | func (w *WebView) RegisterPopupHandler(handler func(string) *WebView) { | |
| 503 | - | w.mu.Lock() | |
| 504 | - | defer w.mu.Unlock() | |
| 505 | - | w.onPopup = handler | |
| 506 | - | } | |
| 507 | - | ||
| 508 | 488 | // RegisterCloseHandler registers a handler for close requests | |
| 509 | 489 | func (w *WebView) RegisterCloseHandler(handler func()) { | |
| 510 | 490 | w.mu.Lock() | |
| @@ -673,9 +653,9 @@ func (w *WebView) ShowPrintDialog() error { | |||
| 673 | 653 | return ErrWebViewDestroyed | |
| 674 | 654 | } | |
| 675 | 655 | ||
| 676 | - | // TODO: Implement print operation | |
| 677 | - | // printOp := webkit.NewPrintOperation(w.view) | |
| 678 | - | // printOp.RunDialog(nil) | |
| 656 | + | // Create and run print operation with GTK print dialog | |
| 657 | + | printOp := webkit.NewPrintOperation(w.view) | |
| 658 | + | printOp.RunDialog(nil) // nil = no parent window, uses active window | |
| 679 | 659 | return nil | |
| 680 | 660 | } | |
| 681 | 661 | ||
| @@ -37,7 +37,6 @@ type WebView struct { | |||||
| 37 | onFaviconChanged func([]byte) | 37 | onFaviconChanged func([]byte) | ||
| 38 | onFaviconURIChanged func(pageURI, faviconURI string) | 38 | onFaviconURIChanged func(pageURI, faviconURI string) | ||
| 39 | onZoomChanged func(float64) | 39 | onZoomChanged func(float64) | ||
| 40 | - | onPopup func(string) *WebView // Deprecated: Use onPopupCreate instead | |||
| 41 | onPopupCreate func(*webkit.NavigationAction) *WebView // New WebKit create signal handler | 40 | onPopupCreate func(*webkit.NavigationAction) *WebView // New WebKit create signal handler | ||
| 42 | onReadyToShow func() // WebKit ready-to-show signal handler | 41 | onReadyToShow func() // WebKit ready-to-show signal handler | ||
| 43 | onClose func() | 42 | onClose func() | ||
| @@ -158,18 +157,6 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||||
| 158 | return wv, nil | 157 | return wv, nil | ||
| 159 | } | 158 | } | ||
| 160 | 159 | ||||
| 161 | - | // NewWebViewWithRelated creates a new WebView related to an existing one | |||
| 162 | - | // (shares process context) | |||
| 163 | - | func NewWebViewWithRelated(cfg *Config, related *WebView) (*WebView, error) { | |||
| 164 | - | if related == nil { | |||
| 165 | - | return NewWebView(cfg) | |||
| 166 | - | } | |||
| 167 | - | ||||
| 168 | - | // TODO: Implement related view creation using webkit.NewWebViewWithRelatedView | |||
| 169 | - | // For now, create a regular view | |||
| 170 | - | return NewWebView(cfg) | |||
| 171 | - | } | |||
| 172 | - | ||||
| 173 | // applyConfig applies the configuration to the WebView settings | 160 | // applyConfig applies the configuration to the WebView settings | ||
| 174 | func (w *WebView) applyConfig() error { | 161 | func (w *WebView) applyConfig() error { | ||
| 175 | settings := w.view.Settings() | 162 | settings := w.view.Settings() | ||
| @@ -498,13 +485,6 @@ func (w *WebView) RegisterZoomChangedHandler(handler func(float64)) { | |||||
| 498 | w.onZoomChanged = handler | 485 | w.onZoomChanged = handler | ||
| 499 | } | 486 | } | ||
| 500 | 487 | ||||
| 501 | - | // RegisterPopupHandler registers a handler for popup requests | |||
| 502 | - | func (w *WebView) RegisterPopupHandler(handler func(string) *WebView) { | |||
| 503 | - | w.mu.Lock() | |||
| 504 | - | defer w.mu.Unlock() | |||
| 505 | - | w.onPopup = handler | |||
| 506 | - | } | |||
| 507 | - | ||||
| 508 | // RegisterCloseHandler registers a handler for close requests | 488 | // RegisterCloseHandler registers a handler for close requests | ||
| 509 | func (w *WebView) RegisterCloseHandler(handler func()) { | 489 | func (w *WebView) RegisterCloseHandler(handler func()) { | ||
| 510 | w.mu.Lock() | 490 | w.mu.Lock() | ||
| @@ -673,9 +653,9 @@ func (w *WebView) ShowPrintDialog() error { | |||||
| 673 | return ErrWebViewDestroyed | 653 | return ErrWebViewDestroyed | ||
| 674 | } | 654 | } | ||
| 675 | 655 | ||||
| 676 | - | // TODO: Implement print operation | 656 | + | // Create and run print operation with GTK print dialog |
| 677 | - | // printOp := webkit.NewPrintOperation(w.view) | 657 | + | printOp := webkit.NewPrintOperation(w.view) |
| 678 | - | // printOp.RunDialog(nil) | 658 | + | printOp.RunDialog(nil) // nil = no parent window, uses active window |
| 679 | return nil | 659 | return nil | ||
| 680 | } | 660 | } | ||
| 681 | 661 | ||||