chore: migrate to purego-cef v0.7.1 + compile local JSON filters on cold start
purego-cef migration (v0.7.1): - SetHandlerTraceEnabled removed (handler tracing dropped upstream) - DefaultWindowInfo/DefaultBrowserSettings replaced with zero-value structs - NewKeyEvent replaced with KeyEvent struct literals - handlerSet now implements SafeClient/SafeLifeSpanHandler (typed params) - NewScreenInfo replaced with ScreenInfo struct literal Filtering fix: - On cold start with empty compiled store, compile from local JSON files before falling back to download. Respects auto_update=false for local filter testing.
- commit
- c46cae795de7c1c94a6ea7a86c7222a6b574c9dd
- author
- brice · · 2026-03-28 06:57 UTC
- parent
- defbe8a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +47 -26
M internal/infrastructure/cef/engine_init.go +0 -1
| @@ -31,7 +31,6 @@ func NewEngine(ctx context.Context, cfg config.CEFEngineConfig, transcodingCfg c | |||
| 31 | 31 | multiThreaded := cfg.CEFMultiThreadedMessageLoop() | |
| 32 | 32 | manualPumpMs := cfg.CEFManualPumpIntervalMs() | |
| 33 | 33 | windowlessFrameRate := cfg.CEFWindowlessFrameRate() | |
| 34 | - | purecef.SetHandlerTraceEnabled(cfg.TraceHandlers) | |
| 35 | 34 | ||
| 36 | 35 | settings := prepareCEFSettings(cfg, logger) | |
| 37 | 36 | ||
| @@ -31,7 +31,6 @@ func NewEngine(ctx context.Context, cfg config.CEFEngineConfig, transcodingCfg c | |||||
| 31 | multiThreaded := cfg.CEFMultiThreadedMessageLoop() | 31 | multiThreaded := cfg.CEFMultiThreadedMessageLoop() | ||
| 32 | manualPumpMs := cfg.CEFManualPumpIntervalMs() | 32 | manualPumpMs := cfg.CEFManualPumpIntervalMs() | ||
| 33 | windowlessFrameRate := cfg.CEFWindowlessFrameRate() | 33 | windowlessFrameRate := cfg.CEFWindowlessFrameRate() | ||
| 34 | - | purecef.SetHandlerTraceEnabled(cfg.TraceHandlers) | |||
| 35 | 34 | ||||
| 36 | settings := prepareCEFSettings(cfg, logger) | 35 | settings := prepareCEFSettings(cfg, logger) | ||
| 37 | 36 | ||||
M internal/infrastructure/cef/factory.go +2 -2
| @@ -118,12 +118,12 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||
| 118 | 118 | wv.client = client | |
| 119 | 119 | ||
| 120 | 120 | // Configure WindowInfo for off-screen rendering (OSR). | |
| 121 | - | windowInfo := purecef.DefaultWindowInfo() | |
| 121 | + | windowInfo := purecef.WindowInfo{} | |
| 122 | 122 | windowInfo.WindowlessRenderingEnabled = 1 | |
| 123 | 123 | windowInfo.ExternalBeginFrameEnabled = 1 | |
| 124 | 124 | ||
| 125 | 125 | // Configure BrowserSettings. | |
| 126 | - | settings := purecef.DefaultBrowserSettings() | |
| 126 | + | settings := purecef.BrowserSettings{} | |
| 127 | 127 | settings.WindowlessFrameRate = f.windowlessFrameRate | |
| 128 | 128 | settings.LocalStorage = 1 // CEF_STATE_ENABLED | |
| 129 | 129 | if bg := f.bgColor.Load(); bg != 0 { | |
| @@ -118,12 +118,12 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||||
| 118 | wv.client = client | 118 | wv.client = client | ||
| 119 | 119 | ||||
| 120 | // Configure WindowInfo for off-screen rendering (OSR). | 120 | // Configure WindowInfo for off-screen rendering (OSR). | ||
| 121 | - | windowInfo := purecef.DefaultWindowInfo() | 121 | + | windowInfo := purecef.WindowInfo{} |
| 122 | windowInfo.WindowlessRenderingEnabled = 1 | 122 | windowInfo.WindowlessRenderingEnabled = 1 | ||
| 123 | windowInfo.ExternalBeginFrameEnabled = 1 | 123 | windowInfo.ExternalBeginFrameEnabled = 1 | ||
| 124 | 124 | ||||
| 125 | // Configure BrowserSettings. | 125 | // Configure BrowserSettings. | ||
| 126 | - | settings := purecef.DefaultBrowserSettings() | 126 | + | settings := purecef.BrowserSettings{} |
| 127 | settings.WindowlessFrameRate = f.windowlessFrameRate | 127 | settings.WindowlessFrameRate = f.windowlessFrameRate | ||
| 128 | settings.LocalStorage = 1 // CEF_STATE_ENABLED | 128 | settings.LocalStorage = 1 // CEF_STATE_ENABLED | ||
| 129 | if bg := f.bgColor.Load(); bg != 0 { | 129 | if bg := f.bgColor.Load(); bg != 0 { | ||
M internal/infrastructure/cef/input_bridge.go +12 -10
| @@ -385,9 +385,12 @@ func (ib *inputBridge) onIMCommit(text string) { | |||
| 385 | 385 | continue | |
| 386 | 386 | } | |
| 387 | 387 | ch := uint16(r) | |
| 388 | - | evt := purecef.NewKeyEvent(purecef.KeyEventTypeKeyeventChar, int32(ch), 0, 0) | |
| 389 | - | evt.Character = ch | |
| 390 | - | evt.UnmodifiedCharacter = ch | |
| 388 | + | evt := purecef.KeyEvent{ | |
| 389 | + | Type: purecef.KeyEventTypeKeyeventChar, | |
| 390 | + | WindowsKeyCode: int32(ch), | |
| 391 | + | Character: ch, | |
| 392 | + | UnmodifiedCharacter: ch, | |
| 393 | + | } | |
| 391 | 394 | host.SendKeyEvent(&evt) | |
| 392 | 395 | } | |
| 393 | 396 | } | |
| @@ -487,13 +490,12 @@ func buildMouseEvent(x, y float64, gdkMods uint, scale int32) purecef.MouseEvent | |||
| 487 | 490 | } | |
| 488 | 491 | ||
| 489 | 492 | func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent { | |
| 490 | - | evt := purecef.NewKeyEvent( | |
| 491 | - | eventType, | |
| 492 | - | gdkKeyvalToWindowsVK(keyval), | |
| 493 | - | int32(keycode), | |
| 494 | - | translateModifiers(gdkMods), | |
| 495 | - | ) | |
| 496 | - | return evt | |
| 493 | + | return purecef.KeyEvent{ | |
| 494 | + | Type: eventType, | |
| 495 | + | WindowsKeyCode: gdkKeyvalToWindowsVK(keyval), | |
| 496 | + | NativeKeyCode: int32(keycode), | |
| 497 | + | Modifiers: translateModifiers(gdkMods), | |
| 498 | + | } | |
| 497 | 499 | } | |
| 498 | 500 | ||
| 499 | 501 | // translateModifiers maps GDK modifier masks to CEF EventFlags values. | |
| @@ -385,9 +385,12 @@ func (ib *inputBridge) onIMCommit(text string) { | |||||
| 385 | continue | 385 | continue | ||
| 386 | } | 386 | } | ||
| 387 | ch := uint16(r) | 387 | ch := uint16(r) | ||
| 388 | - | evt := purecef.NewKeyEvent(purecef.KeyEventTypeKeyeventChar, int32(ch), 0, 0) | 388 | + | evt := purecef.KeyEvent{ |
| 389 | - | evt.Character = ch | 389 | + | Type: purecef.KeyEventTypeKeyeventChar, |
| 390 | - | evt.UnmodifiedCharacter = ch | 390 | + | WindowsKeyCode: int32(ch), |
| 391 | + | Character: ch, | |||
| 392 | + | UnmodifiedCharacter: ch, | |||
| 393 | + | } | |||
| 391 | host.SendKeyEvent(&evt) | 394 | host.SendKeyEvent(&evt) | ||
| 392 | } | 395 | } | ||
| 393 | } | 396 | } | ||
| @@ -487,13 +490,12 @@ func buildMouseEvent(x, y float64, gdkMods uint, scale int32) purecef.MouseEvent | |||||
| 487 | } | 490 | } | ||
| 488 | 491 | ||||
| 489 | func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent { | 492 | func buildKeyEvent(keyval, keycode, gdkMods uint, eventType purecef.KeyEventType) purecef.KeyEvent { | ||
| 490 | - | evt := purecef.NewKeyEvent( | 493 | + | return purecef.KeyEvent{ |
| 491 | - | eventType, | 494 | + | Type: eventType, |
| 492 | - | gdkKeyvalToWindowsVK(keyval), | 495 | + | WindowsKeyCode: gdkKeyvalToWindowsVK(keyval), |
| 493 | - | int32(keycode), | 496 | + | NativeKeyCode: int32(keycode), |
| 494 | - | translateModifiers(gdkMods), | 497 | + | Modifiers: translateModifiers(gdkMods), |
| 495 | - | ) | 498 | + | } |
| 496 | - | return evt | |||
| 497 | } | 499 | } | ||
| 498 | 500 | ||||
| 499 | // translateModifiers maps GDK modifier masks to CEF EventFlags values. | 501 | // translateModifiers maps GDK modifier masks to CEF EventFlags values. | ||
M internal/infrastructure/cef/webview.go +2 -2
| @@ -391,8 +391,8 @@ func (wv *WebView) OpenDevTools() { | |||
| 391 | 391 | if host == nil { | |
| 392 | 392 | return | |
| 393 | 393 | } | |
| 394 | - | windowInfo := purecef.DefaultWindowInfo() | |
| 395 | - | settings := purecef.DefaultBrowserSettings() | |
| 394 | + | windowInfo := purecef.WindowInfo{} | |
| 395 | + | settings := purecef.BrowserSettings{} | |
| 396 | 396 | host.ShowDevTools(&windowInfo, nil, &settings, nil) | |
| 397 | 397 | } | |
| 398 | 398 | ||
| @@ -391,8 +391,8 @@ func (wv *WebView) OpenDevTools() { | |||||
| 391 | if host == nil { | 391 | if host == nil { | ||
| 392 | return | 392 | return | ||
| 393 | } | 393 | } | ||
| 394 | - | windowInfo := purecef.DefaultWindowInfo() | 394 | + | windowInfo := purecef.WindowInfo{} |
| 395 | - | settings := purecef.DefaultBrowserSettings() | 395 | + | settings := purecef.BrowserSettings{} |
| 396 | host.ShowDevTools(&windowInfo, nil, &settings, nil) | 396 | host.ShowDevTools(&windowInfo, nil, &settings, nil) | ||
| 397 | } | 397 | } | ||
| 398 | 398 | ||||
M internal/infrastructure/cef/webview_handlers.go +17 -11
| @@ -27,15 +27,15 @@ type handlerSet struct { | |||
| 27 | 27 | ||
| 28 | 28 | // Compile-time interface checks. | |
| 29 | 29 | var ( | |
| 30 | - | _ purecef.Client = (*handlerSet)(nil) | |
| 31 | - | _ purecef.RenderHandler = (*handlerSet)(nil) | |
| 32 | - | _ purecef.DisplayHandler = (*handlerSet)(nil) | |
| 33 | - | _ purecef.LoadHandler = (*handlerSet)(nil) | |
| 34 | - | _ purecef.LifeSpanHandler = (*handlerSet)(nil) | |
| 35 | - | _ purecef.RequestHandler = (*handlerSet)(nil) | |
| 36 | - | _ purecef.AudioHandler = (*handlerSet)(nil) | |
| 37 | - | _ purecef.ContextMenuHandler = (*handlerSet)(nil) | |
| 38 | - | _ purecef.FindHandler = (*handlerSet)(nil) | |
| 30 | + | _ purecef.SafeClient = (*handlerSet)(nil) | |
| 31 | + | _ purecef.RenderHandler = (*handlerSet)(nil) | |
| 32 | + | _ purecef.DisplayHandler = (*handlerSet)(nil) | |
| 33 | + | _ purecef.LoadHandler = (*handlerSet)(nil) | |
| 34 | + | _ purecef.SafeLifeSpanHandler = (*handlerSet)(nil) | |
| 35 | + | _ purecef.RequestHandler = (*handlerSet)(nil) | |
| 36 | + | _ purecef.AudioHandler = (*handlerSet)(nil) | |
| 37 | + | _ purecef.ContextMenuHandler = (*handlerSet)(nil) | |
| 38 | + | _ purecef.FindHandler = (*handlerSet)(nil) | |
| 39 | 39 | ) | |
| 40 | 40 | ||
| 41 | 41 | // =========================================================================== | |
| @@ -66,7 +66,7 @@ func (h *handlerSet) GetFrameHandler() purecef.FrameHandler { return n | |||
| 66 | 66 | func (h *handlerSet) GetPermissionHandler() purecef.PermissionHandler { return nil } | |
| 67 | 67 | func (h *handlerSet) GetJsdialogHandler() purecef.JsdialogHandler { return nil } | |
| 68 | 68 | func (h *handlerSet) GetKeyboardHandler() purecef.KeyboardHandler { return nil } | |
| 69 | - | func (h *handlerSet) GetLifeSpanHandler() purecef.LifeSpanHandler { return h } | |
| 69 | + | func (h *handlerSet) GetLifeSpanHandler() purecef.SafeLifeSpanHandler { return h } | |
| 70 | 70 | func (h *handlerSet) GetLoadHandler() purecef.LoadHandler { return h } | |
| 71 | 71 | func (h *handlerSet) GetPrintHandler() purecef.PrintHandler { return nil } | |
| 72 | 72 | func (h *handlerSet) GetRenderHandler() purecef.RenderHandler { return h } | |
| @@ -145,7 +145,13 @@ func (h *handlerSet) GetScreenInfo(_ purecef.Browser, info *purecef.ScreenInfo) | |||
| 145 | 145 | screenDepthPerComponent = 8 | |
| 146 | 146 | ) | |
| 147 | 147 | r := purecef.Rect{X: 0, Y: 0, Width: w, Height: ht} | |
| 148 | - | *info = purecef.NewScreenInfo(float32(s), screenDepth, screenDepthPerComponent, false, r, r) | |
| 148 | + | *info = purecef.ScreenInfo{ | |
| 149 | + | DeviceScaleFactor: float32(s), | |
| 150 | + | Depth: screenDepth, | |
| 151 | + | DepthPerComponent: screenDepthPerComponent, | |
| 152 | + | Rect: r, | |
| 153 | + | AvailableRect: r, | |
| 154 | + | } | |
| 149 | 155 | if h.wv != nil && h.wv.ctx != nil { | |
| 150 | 156 | logging.FromContext(h.wv.ctx).Debug(). | |
| 151 | 157 | Uint64("screen_info_seq", callSeq). | |
| @@ -27,15 +27,15 @@ type handlerSet struct { | |||||
| 27 | 27 | ||||
| 28 | // Compile-time interface checks. | 28 | // Compile-time interface checks. | ||
| 29 | var ( | 29 | var ( | ||
| 30 | - | _ purecef.Client = (*handlerSet)(nil) | 30 | + | _ purecef.SafeClient = (*handlerSet)(nil) |
| 31 | - | _ purecef.RenderHandler = (*handlerSet)(nil) | 31 | + | _ purecef.RenderHandler = (*handlerSet)(nil) |
| 32 | - | _ purecef.DisplayHandler = (*handlerSet)(nil) | 32 | + | _ purecef.DisplayHandler = (*handlerSet)(nil) |
| 33 | - | _ purecef.LoadHandler = (*handlerSet)(nil) | 33 | + | _ purecef.LoadHandler = (*handlerSet)(nil) |
| 34 | - | _ purecef.LifeSpanHandler = (*handlerSet)(nil) | 34 | + | _ purecef.SafeLifeSpanHandler = (*handlerSet)(nil) |
| 35 | - | _ purecef.RequestHandler = (*handlerSet)(nil) | 35 | + | _ purecef.RequestHandler = (*handlerSet)(nil) |
| 36 | - | _ purecef.AudioHandler = (*handlerSet)(nil) | 36 | + | _ purecef.AudioHandler = (*handlerSet)(nil) |
| 37 | - | _ purecef.ContextMenuHandler = (*handlerSet)(nil) | 37 | + | _ purecef.ContextMenuHandler = (*handlerSet)(nil) |
| 38 | - | _ purecef.FindHandler = (*handlerSet)(nil) | 38 | + | _ purecef.FindHandler = (*handlerSet)(nil) |
| 39 | ) | 39 | ) | ||
| 40 | 40 | ||||
| 41 | // =========================================================================== | 41 | // =========================================================================== | ||
| @@ -66,7 +66,7 @@ func (h *handlerSet) GetFrameHandler() purecef.FrameHandler { return n | |||||
| 66 | func (h *handlerSet) GetPermissionHandler() purecef.PermissionHandler { return nil } | 66 | func (h *handlerSet) GetPermissionHandler() purecef.PermissionHandler { return nil } | ||
| 67 | func (h *handlerSet) GetJsdialogHandler() purecef.JsdialogHandler { return nil } | 67 | func (h *handlerSet) GetJsdialogHandler() purecef.JsdialogHandler { return nil } | ||
| 68 | func (h *handlerSet) GetKeyboardHandler() purecef.KeyboardHandler { return nil } | 68 | func (h *handlerSet) GetKeyboardHandler() purecef.KeyboardHandler { return nil } | ||
| 69 | - | func (h *handlerSet) GetLifeSpanHandler() purecef.LifeSpanHandler { return h } | 69 | + | func (h *handlerSet) GetLifeSpanHandler() purecef.SafeLifeSpanHandler { return h } |
| 70 | func (h *handlerSet) GetLoadHandler() purecef.LoadHandler { return h } | 70 | func (h *handlerSet) GetLoadHandler() purecef.LoadHandler { return h } | ||
| 71 | func (h *handlerSet) GetPrintHandler() purecef.PrintHandler { return nil } | 71 | func (h *handlerSet) GetPrintHandler() purecef.PrintHandler { return nil } | ||
| 72 | func (h *handlerSet) GetRenderHandler() purecef.RenderHandler { return h } | 72 | func (h *handlerSet) GetRenderHandler() purecef.RenderHandler { return h } | ||
| @@ -145,7 +145,13 @@ func (h *handlerSet) GetScreenInfo(_ purecef.Browser, info *purecef.ScreenInfo) | |||||
| 145 | screenDepthPerComponent = 8 | 145 | screenDepthPerComponent = 8 | ||
| 146 | ) | 146 | ) | ||
| 147 | r := purecef.Rect{X: 0, Y: 0, Width: w, Height: ht} | 147 | r := purecef.Rect{X: 0, Y: 0, Width: w, Height: ht} | ||
| 148 | - | *info = purecef.NewScreenInfo(float32(s), screenDepth, screenDepthPerComponent, false, r, r) | 148 | + | *info = purecef.ScreenInfo{ |
| 149 | + | DeviceScaleFactor: float32(s), | |||
| 150 | + | Depth: screenDepth, | |||
| 151 | + | DepthPerComponent: screenDepthPerComponent, | |||
| 152 | + | Rect: r, | |||
| 153 | + | AvailableRect: r, | |||
| 154 | + | } | |||
| 149 | if h.wv != nil && h.wv.ctx != nil { | 155 | if h.wv != nil && h.wv.ctx != nil { | ||
| 150 | logging.FromContext(h.wv.ctx).Debug(). | 156 | logging.FromContext(h.wv.ctx).Debug(). | ||
| 151 | Uint64("screen_info_seq", callSeq). | 157 | Uint64("screen_info_seq", callSeq). | ||
M internal/infrastructure/filtering/manager.go +14 -0
| @@ -169,6 +169,20 @@ func (m *Manager) loadAsyncWorker(ctx context.Context) { | |||
| 169 | 169 | return | |
| 170 | 170 | } | |
| 171 | 171 | ||
| 172 | + | // If local JSON files exist on disk, compile from them directly instead | |
| 173 | + | // of downloading. This respects auto_update=false on cold start with an | |
| 174 | + | // empty compiled store and allows testing with patched local JSON. | |
| 175 | + | if paths := m.downloader.GetCachedFilterPaths(); len(paths) > 0 { | |
| 176 | + | log.Info().Int("files", len(paths)).Msg("compiling filters from local JSON (skipping download)") | |
| 177 | + | filters, err := m.compileFilterParts(ctx, paths) | |
| 178 | + | if err == nil { | |
| 179 | + | version := m.setActiveFilters(filters, "Filters active") | |
| 180 | + | log.Info().Str("version", version).Int("parts", len(filters)).Msg("filters compiled from local JSON") | |
| 181 | + | return | |
| 182 | + | } | |
| 183 | + | log.Warn().Err(err).Msg("failed to compile local JSON, falling back to download") | |
| 184 | + | } | |
| 185 | + | ||
| 172 | 186 | m.downloadCompileAndActivate(ctx) | |
| 173 | 187 | } | |
| 174 | 188 | ||
| @@ -169,6 +169,20 @@ func (m *Manager) loadAsyncWorker(ctx context.Context) { | |||||
| 169 | return | 169 | return | ||
| 170 | } | 170 | } | ||
| 171 | 171 | ||||
| 172 | + | // If local JSON files exist on disk, compile from them directly instead | |||
| 173 | + | // of downloading. This respects auto_update=false on cold start with an | |||
| 174 | + | // empty compiled store and allows testing with patched local JSON. | |||
| 175 | + | if paths := m.downloader.GetCachedFilterPaths(); len(paths) > 0 { | |||
| 176 | + | log.Info().Int("files", len(paths)).Msg("compiling filters from local JSON (skipping download)") | |||
| 177 | + | filters, err := m.compileFilterParts(ctx, paths) | |||
| 178 | + | if err == nil { | |||
| 179 | + | version := m.setActiveFilters(filters, "Filters active") | |||
| 180 | + | log.Info().Str("version", version).Int("parts", len(filters)).Msg("filters compiled from local JSON") | |||
| 181 | + | return | |||
| 182 | + | } | |||
| 183 | + | log.Warn().Err(err).Msg("failed to compile local JSON, falling back to download") | |||
| 184 | + | } | |||
| 185 | + | ||||
| 172 | m.downloadCompileAndActivate(ctx) | 186 | m.downloadCompileAndActivate(ctx) | ||
| 173 | } | 187 | } | ||
| 174 | 188 | ||||