refactor(bootstrap): replace WebKitStack with Engine interface
- Add temporary internal accessors to webkit.Engine (InternalContext, InternalPool, InternalSettings, InternalInjector, InternalMessageRouter, InternalFilterManager) as M3->M4 bridge until ui.Dependencies is updated - Create BuildEngine() dispatch function in bootstrap/engine.go - Rename ParallelDBWebKit* -> ParallelDBEngine* in gui.go and wire to BuildEngine - Update main.go: initStackAndRepos returns port.Engine, buildAndConfigureApp and buildUIDependencies take port.Engine, type-assert to *webkit.Engine for concrete field access - Delete webkit_stack.go
- commit
- 4f0e3ae5f1f50f0a34d3506c17ece4e2485e0296
- author
- brice · · 2026-03-15 16:31 UTC
- parent
- 324f8f3
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
5 files · +110 -333
M cmd/dumber/main.go +31 -21
| @@ -25,6 +25,7 @@ import ( | |||
| 25 | 25 | "github.com/bnema/dumber/internal/infrastructure/idle" | |
| 26 | 26 | "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite" | |
| 27 | 27 | "github.com/bnema/dumber/internal/infrastructure/updater" | |
| 28 | + | "github.com/bnema/dumber/internal/infrastructure/webkit" | |
| 28 | 29 | "github.com/bnema/dumber/internal/infrastructure/xdg" | |
| 29 | 30 | "github.com/bnema/dumber/internal/logging" | |
| 30 | 31 | "github.com/bnema/dumber/internal/ui" | |
| @@ -94,7 +95,7 @@ func runGUI() int { | |||
| 94 | 95 | ||
| 95 | 96 | needsEagerDB := restoreSessionID != "" || cfg.Session.AutoRestore | |
| 96 | 97 | ||
| 97 | - | stack, repos, dbCleanup, err := initStackAndRepos(ctx, cfg, initResult, needsEagerDB) | |
| 98 | + | engine, repos, dbCleanup, err := initStackAndRepos(ctx, cfg, initResult, needsEagerDB) | |
| 98 | 99 | if err != nil { | |
| 99 | 100 | bootstrapLog.Fatal().Err(err).Msg("failed to initialize database/webkit") | |
| 100 | 101 | } | |
| @@ -111,8 +112,10 @@ func runGUI() int { | |||
| 111 | 112 | logging.Trace().UpdateLogger(log) | |
| 112 | 113 | logCoreDumpLimits(ctx) | |
| 113 | 114 | ||
| 114 | - | if stack.MessageRouter != nil { | |
| 115 | - | stack.MessageRouter.SetBaseContext(ctx) | |
| 115 | + | if wkEngine, ok := engine.(*webkit.Engine); ok { | |
| 116 | + | if mr := wkEngine.InternalMessageRouter(); mr != nil { | |
| 117 | + | mr.SetBaseContext(ctx) | |
| 118 | + | } | |
| 116 | 119 | } | |
| 117 | 120 | ||
| 118 | 121 | useCases := createUseCases(repos, cfg) | |
| @@ -124,7 +127,7 @@ func runGUI() int { | |||
| 124 | 127 | defer closeIdleInhibitor(idleInhibitor) | |
| 125 | 128 | timer.Mark("use_cases") | |
| 126 | 129 | ||
| 127 | - | app, err := buildAndConfigureApp(ctx, cfg, initResult, &stack, repos, useCases, idleInhibitor, browserSession) | |
| 130 | + | app, err := buildAndConfigureApp(ctx, cfg, initResult, engine, repos, useCases, idleInhibitor, browserSession) | |
| 128 | 131 | if err != nil { | |
| 129 | 132 | log.Error().Err(err).Msg("failed to create application") | |
| 130 | 133 | return 1 | |
| @@ -194,7 +197,7 @@ func buildAndConfigureApp( | |||
| 194 | 197 | ctx context.Context, | |
| 195 | 198 | cfg *config.Config, | |
| 196 | 199 | initResult *bootstrap.ParallelInitResult, | |
| 197 | - | stack *bootstrap.WebKitStack, | |
| 200 | + | engine port.Engine, | |
| 198 | 201 | repos *repositories, | |
| 199 | 202 | useCases *useCases, | |
| 200 | 203 | idleInhibitor port.IdleInhibitor, | |
| @@ -203,7 +206,7 @@ func buildAndConfigureApp( | |||
| 203 | 206 | uiDeps := buildUIDependencies( | |
| 204 | 207 | ctx, cfg, initResult.ThemeManager, | |
| 205 | 208 | initResult.ColorResolver, initResult.AdwaitaDetector, | |
| 206 | - | stack, repos, useCases, idleInhibitor, browserSession.Session.ID, browserSession.UnexpectedCloseReports(), | |
| 209 | + | engine, repos, useCases, idleInhibitor, browserSession.Session.ID, browserSession.UnexpectedCloseReports(), | |
| 207 | 210 | ) | |
| 208 | 211 | configureDeferredInit(uiDeps, cfg, browserSession) | |
| 209 | 212 | return ui.New(uiDeps) | |
| @@ -230,10 +233,10 @@ func initStackAndRepos( | |||
| 230 | 233 | cfg *config.Config, | |
| 231 | 234 | initResult *bootstrap.ParallelInitResult, | |
| 232 | 235 | needsEagerDB bool, | |
| 233 | - | ) (bootstrap.WebKitStack, *repositories, func(), error) { | |
| 236 | + | ) (port.Engine, *repositories, func(), error) { | |
| 234 | 237 | if needsEagerDB { | |
| 235 | - | // Parallel phase 2: Database + WebKit stack initialize concurrently | |
| 236 | - | dbWebKit, err := bootstrap.RunParallelDBWebKit(bootstrap.ParallelDBWebKitInput{ | |
| 238 | + | // Parallel phase 2: Database + engine initialize concurrently | |
| 239 | + | dbEngine, err := bootstrap.RunParallelDBEngine(bootstrap.ParallelDBEngineInput{ | |
| 237 | 240 | Ctx: ctx, | |
| 238 | 241 | Config: cfg, | |
| 239 | 242 | DataDir: initResult.DataDir, | |
| @@ -242,13 +245,13 @@ func initStackAndRepos( | |||
| 242 | 245 | ColorResolver: initResult.ColorResolver, | |
| 243 | 246 | }) | |
| 244 | 247 | if err != nil { | |
| 245 | - | return bootstrap.WebKitStack{}, nil, nil, err | |
| 248 | + | return nil, nil, nil, err | |
| 246 | 249 | } | |
| 247 | - | return dbWebKit.Stack, createRepositories(dbWebKit.DB), dbWebKit.DBCleanup, nil | |
| 250 | + | return dbEngine.Engine, createRepositories(dbEngine.DB), dbEngine.DBCleanup, nil | |
| 248 | 251 | } | |
| 249 | 252 | ||
| 250 | 253 | log := logging.FromContext(ctx) | |
| 251 | - | stack := bootstrap.BuildWebKitStack(bootstrap.WebKitStackInput{ | |
| 254 | + | engine, err := bootstrap.BuildEngine(bootstrap.EngineInput{ | |
| 252 | 255 | Ctx: ctx, | |
| 253 | 256 | Config: cfg, | |
| 254 | 257 | DataDir: initResult.DataDir, | |
| @@ -257,13 +260,16 @@ func initStackAndRepos( | |||
| 257 | 260 | ColorResolver: initResult.ColorResolver, | |
| 258 | 261 | Logger: *log, | |
| 259 | 262 | }) | |
| 263 | + | if err != nil { | |
| 264 | + | return nil, nil, nil, err | |
| 265 | + | } | |
| 260 | 266 | ||
| 261 | 267 | lazyDB, err := bootstrap.CreateLazyDatabase() | |
| 262 | 268 | if err != nil { | |
| 263 | - | return stack, nil, nil, err | |
| 269 | + | return nil, nil, nil, err | |
| 264 | 270 | } | |
| 265 | 271 | dbCleanup := func() { _ = lazyDB.Close() } | |
| 266 | - | return stack, createLazyRepositories(lazyDB), dbCleanup, nil | |
| 272 | + | return engine, createLazyRepositories(lazyDB), dbCleanup, nil | |
| 267 | 273 | } | |
| 268 | 274 | ||
| 269 | 275 | func configureDeferredInit( | |
| @@ -501,13 +507,17 @@ func buildUIDependencies( | |||
| 501 | 507 | themeManager *theme.Manager, | |
| 502 | 508 | colorResolver port.ColorSchemeResolver, | |
| 503 | 509 | adwaitaDetector *colorscheme.AdwaitaDetector, | |
| 504 | - | stack *bootstrap.WebKitStack, | |
| 510 | + | engine port.Engine, | |
| 505 | 511 | repos *repositories, | |
| 506 | 512 | uc *useCases, | |
| 507 | 513 | idleInhibitor port.IdleInhibitor, | |
| 508 | 514 | currentSessionID entity.SessionID, | |
| 509 | 515 | startupCrashReports []string, | |
| 510 | 516 | ) *ui.Dependencies { | |
| 517 | + | // Type-assert to *webkit.Engine to access concrete types for ui.Dependencies. | |
| 518 | + | // This bridge will be removed when ui.Dependencies is updated to use port.Engine (M4). | |
| 519 | + | wkEngine := engine.(*webkit.Engine) | |
| 520 | + | ||
| 511 | 521 | return &ui.Dependencies{ | |
| 512 | 522 | Ctx: ctx, | |
| 513 | 523 | Config: cfg, | |
| @@ -518,12 +528,12 @@ func buildUIDependencies( | |||
| 518 | 528 | ColorResolver: colorResolver, | |
| 519 | 529 | AdwaitaDetector: adwaitaDetector, | |
| 520 | 530 | XDG: xdg.New(), | |
| 521 | - | WebContext: stack.Context, | |
| 522 | - | Pool: stack.Pool, | |
| 523 | - | Settings: stack.Settings, | |
| 524 | - | Injector: stack.Injector, | |
| 525 | - | MessageRouter: stack.MessageRouter, | |
| 526 | - | FilterManager: stack.FilterManager, | |
| 531 | + | WebContext: wkEngine.InternalContext(), | |
| 532 | + | Pool: wkEngine.InternalPool(), | |
| 533 | + | Settings: wkEngine.InternalSettings(), | |
| 534 | + | Injector: wkEngine.InternalInjector(), | |
| 535 | + | MessageRouter: wkEngine.InternalMessageRouter(), | |
| 536 | + | FilterManager: wkEngine.InternalFilterManager(), | |
| 527 | 537 | HistoryRepo: repos.history, | |
| 528 | 538 | FavoriteRepo: repos.favorite, | |
| 529 | 539 | ZoomRepo: repos.zoom, | |
| @@ -25,6 +25,7 @@ import ( | |||||
| 25 | "github.com/bnema/dumber/internal/infrastructure/idle" | 25 | "github.com/bnema/dumber/internal/infrastructure/idle" | ||
| 26 | "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite" | 26 | "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite" | ||
| 27 | "github.com/bnema/dumber/internal/infrastructure/updater" | 27 | "github.com/bnema/dumber/internal/infrastructure/updater" | ||
| 28 | + | "github.com/bnema/dumber/internal/infrastructure/webkit" | |||
| 28 | "github.com/bnema/dumber/internal/infrastructure/xdg" | 29 | "github.com/bnema/dumber/internal/infrastructure/xdg" | ||
| 29 | "github.com/bnema/dumber/internal/logging" | 30 | "github.com/bnema/dumber/internal/logging" | ||
| 30 | "github.com/bnema/dumber/internal/ui" | 31 | "github.com/bnema/dumber/internal/ui" | ||
| @@ -94,7 +95,7 @@ func runGUI() int { | |||||
| 94 | 95 | ||||
| 95 | needsEagerDB := restoreSessionID != "" || cfg.Session.AutoRestore | 96 | needsEagerDB := restoreSessionID != "" || cfg.Session.AutoRestore | ||
| 96 | 97 | ||||
| 97 | - | stack, repos, dbCleanup, err := initStackAndRepos(ctx, cfg, initResult, needsEagerDB) | 98 | + | engine, repos, dbCleanup, err := initStackAndRepos(ctx, cfg, initResult, needsEagerDB) |
| 98 | if err != nil { | 99 | if err != nil { | ||
| 99 | bootstrapLog.Fatal().Err(err).Msg("failed to initialize database/webkit") | 100 | bootstrapLog.Fatal().Err(err).Msg("failed to initialize database/webkit") | ||
| 100 | } | 101 | } | ||
| @@ -111,8 +112,10 @@ func runGUI() int { | |||||
| 111 | logging.Trace().UpdateLogger(log) | 112 | logging.Trace().UpdateLogger(log) | ||
| 112 | logCoreDumpLimits(ctx) | 113 | logCoreDumpLimits(ctx) | ||
| 113 | 114 | ||||
| 114 | - | if stack.MessageRouter != nil { | 115 | + | if wkEngine, ok := engine.(*webkit.Engine); ok { |
| 115 | - | stack.MessageRouter.SetBaseContext(ctx) | 116 | + | if mr := wkEngine.InternalMessageRouter(); mr != nil { |
| 117 | + | mr.SetBaseContext(ctx) | |||
| 118 | + | } | |||
| 116 | } | 119 | } | ||
| 117 | 120 | ||||
| 118 | useCases := createUseCases(repos, cfg) | 121 | useCases := createUseCases(repos, cfg) | ||
| @@ -124,7 +127,7 @@ func runGUI() int { | |||||
| 124 | defer closeIdleInhibitor(idleInhibitor) | 127 | defer closeIdleInhibitor(idleInhibitor) | ||
| 125 | timer.Mark("use_cases") | 128 | timer.Mark("use_cases") | ||
| 126 | 129 | ||||
| 127 | - | app, err := buildAndConfigureApp(ctx, cfg, initResult, &stack, repos, useCases, idleInhibitor, browserSession) | 130 | + | app, err := buildAndConfigureApp(ctx, cfg, initResult, engine, repos, useCases, idleInhibitor, browserSession) |
| 128 | if err != nil { | 131 | if err != nil { | ||
| 129 | log.Error().Err(err).Msg("failed to create application") | 132 | log.Error().Err(err).Msg("failed to create application") | ||
| 130 | return 1 | 133 | return 1 | ||
| @@ -194,7 +197,7 @@ func buildAndConfigureApp( | |||||
| 194 | ctx context.Context, | 197 | ctx context.Context, | ||
| 195 | cfg *config.Config, | 198 | cfg *config.Config, | ||
| 196 | initResult *bootstrap.ParallelInitResult, | 199 | initResult *bootstrap.ParallelInitResult, | ||
| 197 | - | stack *bootstrap.WebKitStack, | 200 | + | engine port.Engine, |
| 198 | repos *repositories, | 201 | repos *repositories, | ||
| 199 | useCases *useCases, | 202 | useCases *useCases, | ||
| 200 | idleInhibitor port.IdleInhibitor, | 203 | idleInhibitor port.IdleInhibitor, | ||
| @@ -203,7 +206,7 @@ func buildAndConfigureApp( | |||||
| 203 | uiDeps := buildUIDependencies( | 206 | uiDeps := buildUIDependencies( | ||
| 204 | ctx, cfg, initResult.ThemeManager, | 207 | ctx, cfg, initResult.ThemeManager, | ||
| 205 | initResult.ColorResolver, initResult.AdwaitaDetector, | 208 | initResult.ColorResolver, initResult.AdwaitaDetector, | ||
| 206 | - | stack, repos, useCases, idleInhibitor, browserSession.Session.ID, browserSession.UnexpectedCloseReports(), | 209 | + | engine, repos, useCases, idleInhibitor, browserSession.Session.ID, browserSession.UnexpectedCloseReports(), |
| 207 | ) | 210 | ) | ||
| 208 | configureDeferredInit(uiDeps, cfg, browserSession) | 211 | configureDeferredInit(uiDeps, cfg, browserSession) | ||
| 209 | return ui.New(uiDeps) | 212 | return ui.New(uiDeps) | ||
| @@ -230,10 +233,10 @@ func initStackAndRepos( | |||||
| 230 | cfg *config.Config, | 233 | cfg *config.Config, | ||
| 231 | initResult *bootstrap.ParallelInitResult, | 234 | initResult *bootstrap.ParallelInitResult, | ||
| 232 | needsEagerDB bool, | 235 | needsEagerDB bool, | ||
| 233 | - | ) (bootstrap.WebKitStack, *repositories, func(), error) { | 236 | + | ) (port.Engine, *repositories, func(), error) { |
| 234 | if needsEagerDB { | 237 | if needsEagerDB { | ||
| 235 | - | // Parallel phase 2: Database + WebKit stack initialize concurrently | 238 | + | // Parallel phase 2: Database + engine initialize concurrently |
| 236 | - | dbWebKit, err := bootstrap.RunParallelDBWebKit(bootstrap.ParallelDBWebKitInput{ | 239 | + | dbEngine, err := bootstrap.RunParallelDBEngine(bootstrap.ParallelDBEngineInput{ |
| 237 | Ctx: ctx, | 240 | Ctx: ctx, | ||
| 238 | Config: cfg, | 241 | Config: cfg, | ||
| 239 | DataDir: initResult.DataDir, | 242 | DataDir: initResult.DataDir, | ||
| @@ -242,13 +245,13 @@ func initStackAndRepos( | |||||
| 242 | ColorResolver: initResult.ColorResolver, | 245 | ColorResolver: initResult.ColorResolver, | ||
| 243 | }) | 246 | }) | ||
| 244 | if err != nil { | 247 | if err != nil { | ||
| 245 | - | return bootstrap.WebKitStack{}, nil, nil, err | 248 | + | return nil, nil, nil, err |
| 246 | } | 249 | } | ||
| 247 | - | return dbWebKit.Stack, createRepositories(dbWebKit.DB), dbWebKit.DBCleanup, nil | 250 | + | return dbEngine.Engine, createRepositories(dbEngine.DB), dbEngine.DBCleanup, nil |
| 248 | } | 251 | } | ||
| 249 | 252 | ||||
| 250 | log := logging.FromContext(ctx) | 253 | log := logging.FromContext(ctx) | ||
| 251 | - | stack := bootstrap.BuildWebKitStack(bootstrap.WebKitStackInput{ | 254 | + | engine, err := bootstrap.BuildEngine(bootstrap.EngineInput{ |
| 252 | Ctx: ctx, | 255 | Ctx: ctx, | ||
| 253 | Config: cfg, | 256 | Config: cfg, | ||
| 254 | DataDir: initResult.DataDir, | 257 | DataDir: initResult.DataDir, | ||
| @@ -257,13 +260,16 @@ func initStackAndRepos( | |||||
| 257 | ColorResolver: initResult.ColorResolver, | 260 | ColorResolver: initResult.ColorResolver, | ||
| 258 | Logger: *log, | 261 | Logger: *log, | ||
| 259 | }) | 262 | }) | ||
| 263 | + | if err != nil { | |||
| 264 | + | return nil, nil, nil, err | |||
| 265 | + | } | |||
| 260 | 266 | ||||
| 261 | lazyDB, err := bootstrap.CreateLazyDatabase() | 267 | lazyDB, err := bootstrap.CreateLazyDatabase() | ||
| 262 | if err != nil { | 268 | if err != nil { | ||
| 263 | - | return stack, nil, nil, err | 269 | + | return nil, nil, nil, err |
| 264 | } | 270 | } | ||
| 265 | dbCleanup := func() { _ = lazyDB.Close() } | 271 | dbCleanup := func() { _ = lazyDB.Close() } | ||
| 266 | - | return stack, createLazyRepositories(lazyDB), dbCleanup, nil | 272 | + | return engine, createLazyRepositories(lazyDB), dbCleanup, nil |
| 267 | } | 273 | } | ||
| 268 | 274 | ||||
| 269 | func configureDeferredInit( | 275 | func configureDeferredInit( | ||
| @@ -501,13 +507,17 @@ func buildUIDependencies( | |||||
| 501 | themeManager *theme.Manager, | 507 | themeManager *theme.Manager, | ||
| 502 | colorResolver port.ColorSchemeResolver, | 508 | colorResolver port.ColorSchemeResolver, | ||
| 503 | adwaitaDetector *colorscheme.AdwaitaDetector, | 509 | adwaitaDetector *colorscheme.AdwaitaDetector, | ||
| 504 | - | stack *bootstrap.WebKitStack, | 510 | + | engine port.Engine, |
| 505 | repos *repositories, | 511 | repos *repositories, | ||
| 506 | uc *useCases, | 512 | uc *useCases, | ||
| 507 | idleInhibitor port.IdleInhibitor, | 513 | idleInhibitor port.IdleInhibitor, | ||
| 508 | currentSessionID entity.SessionID, | 514 | currentSessionID entity.SessionID, | ||
| 509 | startupCrashReports []string, | 515 | startupCrashReports []string, | ||
| 510 | ) *ui.Dependencies { | 516 | ) *ui.Dependencies { | ||
| 517 | + | // Type-assert to *webkit.Engine to access concrete types for ui.Dependencies. | |||
| 518 | + | // This bridge will be removed when ui.Dependencies is updated to use port.Engine (M4). | |||
| 519 | + | wkEngine := engine.(*webkit.Engine) | |||
| 520 | + | ||||
| 511 | return &ui.Dependencies{ | 521 | return &ui.Dependencies{ | ||
| 512 | Ctx: ctx, | 522 | Ctx: ctx, | ||
| 513 | Config: cfg, | 523 | Config: cfg, | ||
| @@ -518,12 +528,12 @@ func buildUIDependencies( | |||||
| 518 | ColorResolver: colorResolver, | 528 | ColorResolver: colorResolver, | ||
| 519 | AdwaitaDetector: adwaitaDetector, | 529 | AdwaitaDetector: adwaitaDetector, | ||
| 520 | XDG: xdg.New(), | 530 | XDG: xdg.New(), | ||
| 521 | - | WebContext: stack.Context, | 531 | + | WebContext: wkEngine.InternalContext(), |
| 522 | - | Pool: stack.Pool, | 532 | + | Pool: wkEngine.InternalPool(), |
| 523 | - | Settings: stack.Settings, | 533 | + | Settings: wkEngine.InternalSettings(), |
| 524 | - | Injector: stack.Injector, | 534 | + | Injector: wkEngine.InternalInjector(), |
| 525 | - | MessageRouter: stack.MessageRouter, | 535 | + | MessageRouter: wkEngine.InternalMessageRouter(), |
| 526 | - | FilterManager: stack.FilterManager, | 536 | + | FilterManager: wkEngine.InternalFilterManager(), |
| 527 | HistoryRepo: repos.history, | 537 | HistoryRepo: repos.history, | ||
| 528 | FavoriteRepo: repos.favorite, | 538 | FavoriteRepo: repos.favorite, | ||
| 529 | ZoomRepo: repos.zoom, | 539 | ZoomRepo: repos.zoom, | ||
A internal/bootstrap/engine.go +50 -0
| @@ -0,0 +1,50 @@ | |||
| 1 | + | package bootstrap | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "fmt" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/dumber/internal/application/port" | |
| 8 | + | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 9 | + | "github.com/bnema/dumber/internal/infrastructure/webkit" | |
| 10 | + | "github.com/bnema/dumber/internal/ui/theme" | |
| 11 | + | "github.com/rs/zerolog" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // EngineInput holds the input for BuildEngine. | |
| 15 | + | type EngineInput struct { | |
| 16 | + | Ctx context.Context | |
| 17 | + | Config *config.Config | |
| 18 | + | DataDir string | |
| 19 | + | CacheDir string | |
| 20 | + | ThemeManager *theme.Manager | |
| 21 | + | ColorResolver port.ColorSchemeResolver | |
| 22 | + | Logger zerolog.Logger | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // BuildEngine constructs a port.Engine for the engine type specified in cfg.Engine.Type. | |
| 26 | + | // Currently only "webkit" is supported; "cef" and other types return an error. | |
| 27 | + | func BuildEngine(input EngineInput) (port.Engine, error) { | |
| 28 | + | cfg := input.Config | |
| 29 | + | engineType := cfg.Engine.Type | |
| 30 | + | if engineType == "" { | |
| 31 | + | engineType = "webkit" | |
| 32 | + | } | |
| 33 | + | switch engineType { | |
| 34 | + | case "webkit": | |
| 35 | + | opts := port.EngineOptions{ | |
| 36 | + | DataDir: input.DataDir, | |
| 37 | + | CacheDir: input.CacheDir, | |
| 38 | + | CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy), | |
| 39 | + | } | |
| 40 | + | wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit) | |
| 41 | + | return webkit.NewEngine( | |
| 42 | + | input.Ctx, cfg, opts, wkCfg, | |
| 43 | + | input.ThemeManager, input.ColorResolver, input.Logger, | |
| 44 | + | ) | |
| 45 | + | case "cef": | |
| 46 | + | return nil, fmt.Errorf("CEF engine not yet implemented") | |
| 47 | + | default: | |
| 48 | + | return nil, fmt.Errorf("unknown engine type: %q", engineType) | |
| 49 | + | } | |
| 50 | + | } | |
| @@ -0,0 +1,50 @@ | |||||
| 1 | + | package bootstrap | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "fmt" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 8 | + | "github.com/bnema/dumber/internal/infrastructure/config" | |||
| 9 | + | "github.com/bnema/dumber/internal/infrastructure/webkit" | |||
| 10 | + | "github.com/bnema/dumber/internal/ui/theme" | |||
| 11 | + | "github.com/rs/zerolog" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 14 | + | // EngineInput holds the input for BuildEngine. | |||
| 15 | + | type EngineInput struct { | |||
| 16 | + | Ctx context.Context | |||
| 17 | + | Config *config.Config | |||
| 18 | + | DataDir string | |||
| 19 | + | CacheDir string | |||
| 20 | + | ThemeManager *theme.Manager | |||
| 21 | + | ColorResolver port.ColorSchemeResolver | |||
| 22 | + | Logger zerolog.Logger | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | // BuildEngine constructs a port.Engine for the engine type specified in cfg.Engine.Type. | |||
| 26 | + | // Currently only "webkit" is supported; "cef" and other types return an error. | |||
| 27 | + | func BuildEngine(input EngineInput) (port.Engine, error) { | |||
| 28 | + | cfg := input.Config | |||
| 29 | + | engineType := cfg.Engine.Type | |||
| 30 | + | if engineType == "" { | |||
| 31 | + | engineType = "webkit" | |||
| 32 | + | } | |||
| 33 | + | switch engineType { | |||
| 34 | + | case "webkit": | |||
| 35 | + | opts := port.EngineOptions{ | |||
| 36 | + | DataDir: input.DataDir, | |||
| 37 | + | CacheDir: input.CacheDir, | |||
| 38 | + | CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy), | |||
| 39 | + | } | |||
| 40 | + | wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit) | |||
| 41 | + | return webkit.NewEngine( | |||
| 42 | + | input.Ctx, cfg, opts, wkCfg, | |||
| 43 | + | input.ThemeManager, input.ColorResolver, input.Logger, | |||
| 44 | + | ) | |||
| 45 | + | case "cef": | |||
| 46 | + | return nil, fmt.Errorf("CEF engine not yet implemented") | |||
| 47 | + | default: | |||
| 48 | + | return nil, fmt.Errorf("unknown engine type: %q", engineType) | |||
| 49 | + | } | |||
| 50 | + | } | |||
M internal/bootstrap/gui.go +19 -16
| @@ -263,7 +263,7 @@ func OpenDatabase(ctx context.Context) (*DatabaseResult, error) { | |||
| 263 | 263 | // CreateLazyDatabase creates a lazy database provider that defers initialization. | |
| 264 | 264 | // The database is initialized on first access, allowing the UI to render faster. | |
| 265 | 265 | // | |
| 266 | - | // Currently unused: the application uses OpenDatabase with RunParallelDBWebKit for | |
| 266 | + | // Currently unused: the application uses OpenDatabase with RunParallelDBEngine for | |
| 267 | 267 | // eager initialization. This function is kept for potential future use when lazy | |
| 268 | 268 | // initialization past first paint becomes beneficial. | |
| 269 | 269 | func CreateLazyDatabase() (*sqlite.LazyDB, error) { | |
| @@ -274,19 +274,19 @@ func CreateLazyDatabase() (*sqlite.LazyDB, error) { | |||
| 274 | 274 | return sqlite.NewLazyDB(dbPath), nil | |
| 275 | 275 | } | |
| 276 | 276 | ||
| 277 | - | // ParallelDBWebKitResult holds results from parallel DB and WebKit initialization. | |
| 278 | - | type ParallelDBWebKitResult struct { | |
| 277 | + | // ParallelDBEngineResult holds results from parallel DB and engine initialization. | |
| 278 | + | type ParallelDBEngineResult struct { | |
| 279 | 279 | DB *sql.DB | |
| 280 | 280 | DBCleanup func() | |
| 281 | - | Stack WebKitStack | |
| 281 | + | Engine port.Engine | |
| 282 | 282 | } | |
| 283 | 283 | ||
| 284 | - | // ParallelDBWebKitInput holds inputs for parallel DB and WebKit initialization. | |
| 285 | - | type ParallelDBWebKitInput struct { | |
| 284 | + | // ParallelDBEngineInput holds inputs for parallel DB and engine initialization. | |
| 285 | + | type ParallelDBEngineInput struct { | |
| 286 | 286 | Ctx context.Context | |
| 287 | 287 | Config *config.Config | |
| 288 | - | DataDir string // For WebKit context | |
| 289 | - | CacheDir string // For WebKit cache | |
| 288 | + | DataDir string // For engine context | |
| 289 | + | CacheDir string // For engine cache | |
| 290 | 290 | ThemeManager *theme.Manager | |
| 291 | 291 | ColorResolver port.ColorSchemeResolver | |
| 292 | 292 | } | |
| @@ -300,10 +300,10 @@ type dbInitResult struct { | |||
| 300 | 300 | err error | |
| 301 | 301 | } | |
| 302 | 302 | ||
| 303 | - | // RunParallelDBWebKit initializes database in background while WebKit runs on main thread. | |
| 304 | - | // Database init happens in goroutine (pure Go/WASM), WebKit must stay on main thread (GTK). | |
| 305 | - | // This saves ~150ms by overlapping DB migrations with WebKit context creation. | |
| 306 | - | func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, error) { | |
| 303 | + | // RunParallelDBEngine initializes database in background while the engine runs on main thread. | |
| 304 | + | // Database init happens in goroutine (pure Go/WASM), engine must stay on main thread (GTK). | |
| 305 | + | // This saves ~150ms by overlapping DB migrations with engine context creation. | |
| 306 | + | func RunParallelDBEngine(input ParallelDBEngineInput) (*ParallelDBEngineResult, error) { | |
| 307 | 307 | log := logging.FromContext(input.Ctx) | |
| 308 | 308 | ||
| 309 | 309 | dbCh := make(chan dbInitResult, 1) | |
| @@ -321,8 +321,8 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||
| 321 | 321 | } | |
| 322 | 322 | }() | |
| 323 | 323 | ||
| 324 | - | // WebKit stack on main thread (GTK requirement) | |
| 325 | - | stack := BuildWebKitStack(WebKitStackInput{ | |
| 324 | + | // Engine on main thread (GTK requirement) | |
| 325 | + | engine, err := BuildEngine(EngineInput{ | |
| 326 | 326 | Ctx: input.Ctx, | |
| 327 | 327 | Config: input.Config, | |
| 328 | 328 | DataDir: input.DataDir, | |
| @@ -331,6 +331,9 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||
| 331 | 331 | ColorResolver: input.ColorResolver, | |
| 332 | 332 | Logger: *log, | |
| 333 | 333 | }) | |
| 334 | + | if err != nil { | |
| 335 | + | return nil, fmt.Errorf("engine initialization: %w", err) | |
| 336 | + | } | |
| 334 | 337 | ||
| 335 | 338 | // Wait for database | |
| 336 | 339 | dbRes := <-dbCh | |
| @@ -339,9 +342,9 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||
| 339 | 342 | return nil, fmt.Errorf("database initialization: %w", dbRes.err) | |
| 340 | 343 | } | |
| 341 | 344 | ||
| 342 | - | return &ParallelDBWebKitResult{ | |
| 345 | + | return &ParallelDBEngineResult{ | |
| 343 | 346 | DB: dbRes.db, | |
| 344 | 347 | DBCleanup: dbRes.cleanup, | |
| 345 | - | Stack: stack, | |
| 348 | + | Engine: engine, | |
| 346 | 349 | }, nil | |
| 347 | 350 | } | |
| @@ -263,7 +263,7 @@ func OpenDatabase(ctx context.Context) (*DatabaseResult, error) { | |||||
| 263 | // CreateLazyDatabase creates a lazy database provider that defers initialization. | 263 | // CreateLazyDatabase creates a lazy database provider that defers initialization. | ||
| 264 | // The database is initialized on first access, allowing the UI to render faster. | 264 | // The database is initialized on first access, allowing the UI to render faster. | ||
| 265 | // | 265 | // | ||
| 266 | - | // Currently unused: the application uses OpenDatabase with RunParallelDBWebKit for | 266 | + | // Currently unused: the application uses OpenDatabase with RunParallelDBEngine for |
| 267 | // eager initialization. This function is kept for potential future use when lazy | 267 | // eager initialization. This function is kept for potential future use when lazy | ||
| 268 | // initialization past first paint becomes beneficial. | 268 | // initialization past first paint becomes beneficial. | ||
| 269 | func CreateLazyDatabase() (*sqlite.LazyDB, error) { | 269 | func CreateLazyDatabase() (*sqlite.LazyDB, error) { | ||
| @@ -274,19 +274,19 @@ func CreateLazyDatabase() (*sqlite.LazyDB, error) { | |||||
| 274 | return sqlite.NewLazyDB(dbPath), nil | 274 | return sqlite.NewLazyDB(dbPath), nil | ||
| 275 | } | 275 | } | ||
| 276 | 276 | ||||
| 277 | - | // ParallelDBWebKitResult holds results from parallel DB and WebKit initialization. | 277 | + | // ParallelDBEngineResult holds results from parallel DB and engine initialization. |
| 278 | - | type ParallelDBWebKitResult struct { | 278 | + | type ParallelDBEngineResult struct { |
| 279 | DB *sql.DB | 279 | DB *sql.DB | ||
| 280 | DBCleanup func() | 280 | DBCleanup func() | ||
| 281 | - | Stack WebKitStack | 281 | + | Engine port.Engine |
| 282 | } | 282 | } | ||
| 283 | 283 | ||||
| 284 | - | // ParallelDBWebKitInput holds inputs for parallel DB and WebKit initialization. | 284 | + | // ParallelDBEngineInput holds inputs for parallel DB and engine initialization. |
| 285 | - | type ParallelDBWebKitInput struct { | 285 | + | type ParallelDBEngineInput struct { |
| 286 | Ctx context.Context | 286 | Ctx context.Context | ||
| 287 | Config *config.Config | 287 | Config *config.Config | ||
| 288 | - | DataDir string // For WebKit context | 288 | + | DataDir string // For engine context |
| 289 | - | CacheDir string // For WebKit cache | 289 | + | CacheDir string // For engine cache |
| 290 | ThemeManager *theme.Manager | 290 | ThemeManager *theme.Manager | ||
| 291 | ColorResolver port.ColorSchemeResolver | 291 | ColorResolver port.ColorSchemeResolver | ||
| 292 | } | 292 | } | ||
| @@ -300,10 +300,10 @@ type dbInitResult struct { | |||||
| 300 | err error | 300 | err error | ||
| 301 | } | 301 | } | ||
| 302 | 302 | ||||
| 303 | - | // RunParallelDBWebKit initializes database in background while WebKit runs on main thread. | 303 | + | // RunParallelDBEngine initializes database in background while the engine runs on main thread. |
| 304 | - | // Database init happens in goroutine (pure Go/WASM), WebKit must stay on main thread (GTK). | 304 | + | // Database init happens in goroutine (pure Go/WASM), engine must stay on main thread (GTK). |
| 305 | - | // This saves ~150ms by overlapping DB migrations with WebKit context creation. | 305 | + | // This saves ~150ms by overlapping DB migrations with engine context creation. |
| 306 | - | func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, error) { | 306 | + | func RunParallelDBEngine(input ParallelDBEngineInput) (*ParallelDBEngineResult, error) { |
| 307 | log := logging.FromContext(input.Ctx) | 307 | log := logging.FromContext(input.Ctx) | ||
| 308 | 308 | ||||
| 309 | dbCh := make(chan dbInitResult, 1) | 309 | dbCh := make(chan dbInitResult, 1) | ||
| @@ -321,8 +321,8 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||||
| 321 | } | 321 | } | ||
| 322 | }() | 322 | }() | ||
| 323 | 323 | ||||
| 324 | - | // WebKit stack on main thread (GTK requirement) | 324 | + | // Engine on main thread (GTK requirement) |
| 325 | - | stack := BuildWebKitStack(WebKitStackInput{ | 325 | + | engine, err := BuildEngine(EngineInput{ |
| 326 | Ctx: input.Ctx, | 326 | Ctx: input.Ctx, | ||
| 327 | Config: input.Config, | 327 | Config: input.Config, | ||
| 328 | DataDir: input.DataDir, | 328 | DataDir: input.DataDir, | ||
| @@ -331,6 +331,9 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||||
| 331 | ColorResolver: input.ColorResolver, | 331 | ColorResolver: input.ColorResolver, | ||
| 332 | Logger: *log, | 332 | Logger: *log, | ||
| 333 | }) | 333 | }) | ||
| 334 | + | if err != nil { | |||
| 335 | + | return nil, fmt.Errorf("engine initialization: %w", err) | |||
| 336 | + | } | |||
| 334 | 337 | ||||
| 335 | // Wait for database | 338 | // Wait for database | ||
| 336 | dbRes := <-dbCh | 339 | dbRes := <-dbCh | ||
| @@ -339,9 +342,9 @@ func RunParallelDBWebKit(input ParallelDBWebKitInput) (*ParallelDBWebKitResult, | |||||
| 339 | return nil, fmt.Errorf("database initialization: %w", dbRes.err) | 342 | return nil, fmt.Errorf("database initialization: %w", dbRes.err) | ||
| 340 | } | 343 | } | ||
| 341 | 344 | ||||
| 342 | - | return &ParallelDBWebKitResult{ | 345 | + | return &ParallelDBEngineResult{ |
| 343 | DB: dbRes.db, | 346 | DB: dbRes.db, | ||
| 344 | DBCleanup: dbRes.cleanup, | 347 | DBCleanup: dbRes.cleanup, | ||
| 345 | - | Stack: stack, | 348 | + | Engine: engine, |
| 346 | }, nil | 349 | }, nil | ||
| 347 | } | 350 | } | ||
D internal/bootstrap/webkit_stack.go +0 -296
| @@ -1,296 +0,0 @@ | |||
| 1 | - | package bootstrap | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "context" | |
| 5 | - | "path/filepath" | |
| 6 | - | ||
| 7 | - | "github.com/bnema/dumber/assets" | |
| 8 | - | "github.com/bnema/dumber/internal/application/port" | |
| 9 | - | "github.com/bnema/dumber/internal/application/usecase" | |
| 10 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 11 | - | "github.com/bnema/dumber/internal/infrastructure/env" | |
| 12 | - | "github.com/bnema/dumber/internal/infrastructure/filtering" | |
| 13 | - | "github.com/bnema/dumber/internal/infrastructure/webkit" | |
| 14 | - | "github.com/bnema/dumber/internal/logging" | |
| 15 | - | "github.com/bnema/dumber/internal/ui/theme" | |
| 16 | - | "github.com/jwijenbergh/puregotk/v4/gdk" | |
| 17 | - | "github.com/rs/zerolog" | |
| 18 | - | ) | |
| 19 | - | ||
| 20 | - | // WebKitStackInput holds the input for BuildWebKitStack. | |
| 21 | - | type WebKitStackInput struct { | |
| 22 | - | Ctx context.Context | |
| 23 | - | Config *config.Config | |
| 24 | - | DataDir string | |
| 25 | - | CacheDir string | |
| 26 | - | ThemeManager *theme.Manager | |
| 27 | - | ColorResolver port.ColorSchemeResolver | |
| 28 | - | Logger zerolog.Logger | |
| 29 | - | } | |
| 30 | - | ||
| 31 | - | type WebKitStack struct { | |
| 32 | - | Context *webkit.WebKitContext | |
| 33 | - | Settings *webkit.SettingsManager | |
| 34 | - | Injector *webkit.ContentInjector | |
| 35 | - | MessageRouter *webkit.MessageRouter | |
| 36 | - | Pool *webkit.WebViewPool | |
| 37 | - | FilterManager *filtering.Manager | |
| 38 | - | } | |
| 39 | - | ||
| 40 | - | func BuildWebKitStack(input WebKitStackInput) WebKitStack { | |
| 41 | - | ctx := input.Ctx | |
| 42 | - | cfg := input.Config | |
| 43 | - | logger := input.Logger | |
| 44 | - | ||
| 45 | - | perfSettings := surveyHardwareAndResolveProfile(ctx, cfg, logger) | |
| 46 | - | ||
| 47 | - | configureRenderingEnvironment(ctx, cfg, &perfSettings, logger) | |
| 48 | - | logging.Trace().Mark("render_env") | |
| 49 | - | ||
| 50 | - | wkOpts := buildWebKitContextOptions(cfg, input.DataDir, input.CacheDir, &perfSettings) | |
| 51 | - | logger.Info(). | |
| 52 | - | Str("cookie_policy", string(wkOpts.CookiePolicy)). | |
| 53 | - | Bool("itp_enabled", wkOpts.ITPEnabled). | |
| 54 | - | Msg("webkit privacy configuration") | |
| 55 | - | wkCtx, err := webkit.NewWebKitContextWithOptions(ctx, wkOpts) | |
| 56 | - | if err != nil { | |
| 57 | - | logger.Fatal().Err(err).Msg("failed to initialize WebKit context") | |
| 58 | - | } | |
| 59 | - | logging.Trace().Mark("webkit_context") | |
| 60 | - | ||
| 61 | - | filterManager := initFilterManager(ctx, cfg, input.DataDir, logger) | |
| 62 | - | ||
| 63 | - | schemeHandler := webkit.NewDumbSchemeHandler(ctx) | |
| 64 | - | schemeHandler.SetAssets(assets.WebUIAssets) | |
| 65 | - | schemeHandler.RegisterWithContext(wkCtx) | |
| 66 | - | ||
| 67 | - | settings, injector, messageRouter := initWebKitComponents(ctx, cfg, input.ThemeManager, input.ColorResolver, logger) | |
| 68 | - | ||
| 69 | - | pool := initWebViewPool(ctx, wkCtx, settings, injector, messageRouter, input.ThemeManager, filterManager, &perfSettings, logger) | |
| 70 | - | ||
| 71 | - | return WebKitStack{ | |
| 72 | - | Context: wkCtx, | |
| 73 | - | Settings: settings, | |
| 74 | - | Injector: injector, | |
| 75 | - | MessageRouter: messageRouter, | |
| 76 | - | Pool: pool, | |
| 77 | - | FilterManager: filterManager, | |
| 78 | - | } | |
| 79 | - | } | |
| 80 | - | ||
| 81 | - | func surveyHardwareAndResolveProfile(ctx context.Context, cfg *config.Config, logger zerolog.Logger) config.ResolvedPerformanceSettings { | |
| 82 | - | hwSurveyor := env.NewHardwareSurveyor() | |
| 83 | - | hwInfo := hwSurveyor.Survey(ctx) | |
| 84 | - | logging.Trace().Mark("hardware_survey") | |
| 85 | - | logger.Info(). | |
| 86 | - | Int("cpu_cores", hwInfo.CPUCores). | |
| 87 | - | Int("cpu_threads", hwInfo.CPUThreads). | |
| 88 | - | Uint64("ram_mb", hwInfo.TotalRAM/(1024*1024)). | |
| 89 | - | Str("gpu_vendor", string(hwInfo.GPUVendor)). | |
| 90 | - | Uint64("vram_mb", hwInfo.VRAM/(1024*1024)). | |
| 91 | - | Msg("hardware survey completed") | |
| 92 | - | ||
| 93 | - | perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo) | |
| 94 | - | logging.Trace().Mark("performance_profile") | |
| 95 | - | logger.Info(). | |
| 96 | - | Str("profile", string(cfg.Performance.Profile)). | |
| 97 | - | Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads). | |
| 98 | - | Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads). | |
| 99 | - | Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount). | |
| 100 | - | Msg("resolved performance profile") | |
| 101 | - | ||
| 102 | - | return perfSettings | |
| 103 | - | } | |
| 104 | - | ||
| 105 | - | func buildWebKitContextOptions( | |
| 106 | - | cfg *config.Config, | |
| 107 | - | dataDir, cacheDir string, | |
| 108 | - | perfSettings *config.ResolvedPerformanceSettings, | |
| 109 | - | ) port.WebKitContextOptions { | |
| 110 | - | cookiePolicy := port.WebKitCookiePolicyNoThirdParty | |
| 111 | - | itpEnabled := true | |
| 112 | - | if cfg != nil { | |
| 113 | - | cookiePolicy = port.WebKitCookiePolicy(cfg.Privacy.CookiePolicy) | |
| 114 | - | itpEnabled = cfg.Privacy.ITPEnabled | |
| 115 | - | } | |
| 116 | - | ||
| 117 | - | wkOpts := port.WebKitContextOptions{ | |
| 118 | - | DataDir: dataDir, | |
| 119 | - | CacheDir: cacheDir, | |
| 120 | - | CookiePolicy: cookiePolicy, | |
| 121 | - | ITPEnabled: itpEnabled, | |
| 122 | - | } | |
| 123 | - | ||
| 124 | - | if hasWebProcessMemoryConfig(perfSettings) { | |
| 125 | - | wkOpts.WebProcessMemory = &port.MemoryPressureConfig{ | |
| 126 | - | MemoryLimitMB: perfSettings.WebProcessMemoryLimitMB, | |
| 127 | - | PollIntervalSec: perfSettings.WebProcessMemoryPollIntervalSec, | |
| 128 | - | ConservativeThreshold: perfSettings.WebProcessMemoryConservativeThreshold, | |
| 129 | - | StrictThreshold: perfSettings.WebProcessMemoryStrictThreshold, | |
| 130 | - | } | |
| 131 | - | } | |
| 132 | - | ||
| 133 | - | if hasNetworkProcessMemoryConfig(perfSettings) { | |
| 134 | - | wkOpts.NetworkProcessMemory = &port.MemoryPressureConfig{ | |
| 135 | - | MemoryLimitMB: perfSettings.NetworkProcessMemoryLimitMB, | |
| 136 | - | PollIntervalSec: perfSettings.NetworkProcessMemoryPollIntervalSec, | |
| 137 | - | ConservativeThreshold: perfSettings.NetworkProcessMemoryConservativeThreshold, | |
| 138 | - | StrictThreshold: perfSettings.NetworkProcessMemoryStrictThreshold, | |
| 139 | - | } | |
| 140 | - | } | |
| 141 | - | ||
| 142 | - | return wkOpts | |
| 143 | - | } | |
| 144 | - | ||
| 145 | - | func initFilterManager(ctx context.Context, cfg *config.Config, dataDir string, logger zerolog.Logger) *filtering.Manager { | |
| 146 | - | filterStoreDir := filepath.Join(dataDir, "filters", "store") | |
| 147 | - | filterJSONDir := filepath.Join(dataDir, "filters", "json") | |
| 148 | - | filterManager, err := filtering.NewManager(filtering.ManagerConfig{ | |
| 149 | - | StoreDir: filterStoreDir, | |
| 150 | - | JSONDir: filterJSONDir, | |
| 151 | - | Enabled: cfg.ContentFiltering.Enabled, | |
| 152 | - | AutoUpdate: cfg.ContentFiltering.AutoUpdate, | |
| 153 | - | }) | |
| 154 | - | if err != nil { | |
| 155 | - | logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering") | |
| 156 | - | return nil | |
| 157 | - | } | |
| 158 | - | if err := filterManager.Initialize(ctx); err != nil { | |
| 159 | - | logger.Warn().Err(err).Msg("failed to initialize filters, will load async") | |
| 160 | - | } | |
| 161 | - | logging.Trace().Mark("filter_manager") | |
| 162 | - | return filterManager | |
| 163 | - | } | |
| 164 | - | ||
| 165 | - | func initWebKitComponents( | |
| 166 | - | ctx context.Context, | |
| 167 | - | cfg *config.Config, | |
| 168 | - | themeManager *theme.Manager, | |
| 169 | - | colorResolver port.ColorSchemeResolver, | |
| 170 | - | logger zerolog.Logger, | |
| 171 | - | ) (*webkit.SettingsManager, *webkit.ContentInjector, *webkit.MessageRouter) { | |
| 172 | - | settings := webkit.NewSettingsManager(ctx, cfg) | |
| 173 | - | injector := webkit.NewContentInjector(colorResolver) | |
| 174 | - | ||
| 175 | - | injector.SetAutoCopyConfigGetter(func() bool { | |
| 176 | - | return config.Get().Clipboard.AutoCopyOnSelection | |
| 177 | - | }) | |
| 178 | - | ||
| 179 | - | prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector) | |
| 180 | - | themeCSSText := themeManager.GetWebUIThemeCSS() | |
| 181 | - | if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil { | |
| 182 | - | logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS") | |
| 183 | - | } | |
| 184 | - | ||
| 185 | - | messageRouter := webkit.NewMessageRouter(ctx) | |
| 186 | - | logging.Trace().Mark("settings_manager") | |
| 187 | - | ||
| 188 | - | return settings, injector, messageRouter | |
| 189 | - | } | |
| 190 | - | ||
| 191 | - | func initWebViewPool( | |
| 192 | - | ctx context.Context, | |
| 193 | - | wkCtx *webkit.WebKitContext, | |
| 194 | - | settings *webkit.SettingsManager, | |
| 195 | - | injector *webkit.ContentInjector, | |
| 196 | - | messageRouter *webkit.MessageRouter, | |
| 197 | - | themeManager *theme.Manager, | |
| 198 | - | filterManager *filtering.Manager, | |
| 199 | - | perfSettings *config.ResolvedPerformanceSettings, | |
| 200 | - | logger zerolog.Logger, | |
| 201 | - | ) *webkit.WebViewPool { | |
| 202 | - | poolCfg := webkit.DefaultPoolConfig() | |
| 203 | - | if perfSettings.WebViewPoolPrewarmCount > 0 { | |
| 204 | - | poolCfg.PrewarmCount = perfSettings.WebViewPoolPrewarmCount | |
| 205 | - | } | |
| 206 | - | pool := webkit.NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter) | |
| 207 | - | ||
| 208 | - | bgR, bgG, bgB, bgA := themeManager.GetBackgroundRGBA() | |
| 209 | - | pool.SetBackgroundColor(bgR, bgG, bgB, bgA) | |
| 210 | - | ||
| 211 | - | if filterManager != nil { | |
| 212 | - | pool.SetFilterApplier(filterManager) | |
| 213 | - | } | |
| 214 | - | ||
| 215 | - | if shouldPrewarmFirstWebView(gdk.DisplayGetDefault()) { | |
| 216 | - | if err := pool.PrewarmFirst(ctx); err != nil { | |
| 217 | - | logger.Warn().Err(err).Msg("failed to prewarm first webview, first tab may be slower") | |
| 218 | - | } | |
| 219 | - | } else { | |
| 220 | - | logger.Warn().Msg("skipping first webview prewarm: no GDK display available yet") | |
| 221 | - | } | |
| 222 | - | logging.Trace().Mark("pool_prewarm_first") | |
| 223 | - | ||
| 224 | - | return pool | |
| 225 | - | } | |
| 226 | - | ||
| 227 | - | func shouldPrewarmFirstWebView(display *gdk.Display) bool { | |
| 228 | - | return display != nil | |
| 229 | - | } | |
| 230 | - | ||
| 231 | - | // hasWebProcessMemoryConfig returns true if any web process memory setting is configured. | |
| 232 | - | func hasWebProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool { | |
| 233 | - | return p.WebProcessMemoryLimitMB > 0 || | |
| 234 | - | p.WebProcessMemoryPollIntervalSec > 0 || | |
| 235 | - | p.WebProcessMemoryConservativeThreshold > 0 || | |
| 236 | - | p.WebProcessMemoryStrictThreshold > 0 | |
| 237 | - | } | |
| 238 | - | ||
| 239 | - | // hasNetworkProcessMemoryConfig returns true if any network process memory setting is configured. | |
| 240 | - | func hasNetworkProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool { | |
| 241 | - | return p.NetworkProcessMemoryLimitMB > 0 || | |
| 242 | - | p.NetworkProcessMemoryPollIntervalSec > 0 || | |
| 243 | - | p.NetworkProcessMemoryConservativeThreshold > 0 || | |
| 244 | - | p.NetworkProcessMemoryStrictThreshold > 0 | |
| 245 | - | } | |
| 246 | - | ||
| 247 | - | // configureRenderingEnvironment sets up the rendering environment before WebKit/GTK initialization. | |
| 248 | - | // Environment variables must be set before GTK/WebKit/GStreamer initializes. | |
| 249 | - | func configureRenderingEnvironment( | |
| 250 | - | ctx context.Context, | |
| 251 | - | cfg *config.Config, | |
| 252 | - | perfSettings *config.ResolvedPerformanceSettings, | |
| 253 | - | logger zerolog.Logger, | |
| 254 | - | ) { | |
| 255 | - | // Install GLib log handler FIRST if configured (must be before any GTK/GLib calls) | |
| 256 | - | if cfg.Logging.CaptureGTKLogs { | |
| 257 | - | enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace" | |
| 258 | - | logging.InstallGLibLogHandler(ctx, logger, enableDebug) | |
| 259 | - | } | |
| 260 | - | ||
| 261 | - | renderEnv := env.NewManager() | |
| 262 | - | gpuVendor := renderEnv.DetectGPUVendor(ctx) | |
| 263 | - | renderSettings := env.RenderingSettings{ | |
| 264 | - | // GStreamer settings | |
| 265 | - | ForceVSync: cfg.Media.ForceVSync, | |
| 266 | - | GLRenderingMode: string(cfg.Media.GLRenderingMode), | |
| 267 | - | GStreamerDebugLevel: cfg.Media.GStreamerDebugLevel, | |
| 268 | - | ||
| 269 | - | // WebKit compositor settings | |
| 270 | - | DisableDMABufRenderer: cfg.Rendering.DisableDMABufRenderer, | |
| 271 | - | ForceCompositingMode: cfg.Rendering.ForceCompositingMode, | |
| 272 | - | DisableCompositingMode: cfg.Rendering.DisableCompositingMode, | |
| 273 | - | ||
| 274 | - | // GTK/GSK settings | |
| 275 | - | GSKRenderer: string(cfg.Rendering.GSKRenderer), | |
| 276 | - | DisableMipmaps: cfg.Rendering.DisableMipmaps, | |
| 277 | - | PreferGL: cfg.Rendering.PreferGL, | |
| 278 | - | ||
| 279 | - | // Debug settings | |
| 280 | - | ShowFPS: cfg.Rendering.ShowFPS, | |
| 281 | - | SampleMemory: cfg.Rendering.SampleMemory, | |
| 282 | - | DebugFrames: cfg.Rendering.DebugFrames, | |
| 283 | - | ||
| 284 | - | // Skia rendering thread settings (from resolved profile) | |
| 285 | - | SkiaCPUPaintingThreads: perfSettings.SkiaCPUPaintingThreads, | |
| 286 | - | SkiaGPUPaintingThreads: perfSettings.SkiaGPUPaintingThreads, | |
| 287 | - | SkiaEnableCPURendering: perfSettings.SkiaEnableCPURendering, | |
| 288 | - | } | |
| 289 | - | if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil { | |
| 290 | - | logger.Warn().Err(err).Msg("failed to apply rendering environment") | |
| 291 | - | } | |
| 292 | - | logger.Info(). | |
| 293 | - | Str("gpu", string(gpuVendor)). | |
| 294 | - | Interface("vars", renderEnv.GetAppliedVars()). | |
| 295 | - | Msg("rendering environment configured") | |
| 296 | - | } | |
| @@ -1,296 +0,0 @@ | |||||
| 1 | - | package bootstrap | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "context" | |||
| 5 | - | "path/filepath" | |||
| 6 | - | ||||
| 7 | - | "github.com/bnema/dumber/assets" | |||
| 8 | - | "github.com/bnema/dumber/internal/application/port" | |||
| 9 | - | "github.com/bnema/dumber/internal/application/usecase" | |||
| 10 | - | "github.com/bnema/dumber/internal/infrastructure/config" | |||
| 11 | - | "github.com/bnema/dumber/internal/infrastructure/env" | |||
| 12 | - | "github.com/bnema/dumber/internal/infrastructure/filtering" | |||
| 13 | - | "github.com/bnema/dumber/internal/infrastructure/webkit" | |||
| 14 | - | "github.com/bnema/dumber/internal/logging" | |||
| 15 | - | "github.com/bnema/dumber/internal/ui/theme" | |||
| 16 | - | "github.com/jwijenbergh/puregotk/v4/gdk" | |||
| 17 | - | "github.com/rs/zerolog" | |||
| 18 | - | ) | |||
| 19 | - | ||||
| 20 | - | // WebKitStackInput holds the input for BuildWebKitStack. | |||
| 21 | - | type WebKitStackInput struct { | |||
| 22 | - | Ctx context.Context | |||
| 23 | - | Config *config.Config | |||
| 24 | - | DataDir string | |||
| 25 | - | CacheDir string | |||
| 26 | - | ThemeManager *theme.Manager | |||
| 27 | - | ColorResolver port.ColorSchemeResolver | |||
| 28 | - | Logger zerolog.Logger | |||
| 29 | - | } | |||
| 30 | - | ||||
| 31 | - | type WebKitStack struct { | |||
| 32 | - | Context *webkit.WebKitContext | |||
| 33 | - | Settings *webkit.SettingsManager | |||
| 34 | - | Injector *webkit.ContentInjector | |||
| 35 | - | MessageRouter *webkit.MessageRouter | |||
| 36 | - | Pool *webkit.WebViewPool | |||
| 37 | - | FilterManager *filtering.Manager | |||
| 38 | - | } | |||
| 39 | - | ||||
| 40 | - | func BuildWebKitStack(input WebKitStackInput) WebKitStack { | |||
| 41 | - | ctx := input.Ctx | |||
| 42 | - | cfg := input.Config | |||
| 43 | - | logger := input.Logger | |||
| 44 | - | ||||
| 45 | - | perfSettings := surveyHardwareAndResolveProfile(ctx, cfg, logger) | |||
| 46 | - | ||||
| 47 | - | configureRenderingEnvironment(ctx, cfg, &perfSettings, logger) | |||
| 48 | - | logging.Trace().Mark("render_env") | |||
| 49 | - | ||||
| 50 | - | wkOpts := buildWebKitContextOptions(cfg, input.DataDir, input.CacheDir, &perfSettings) | |||
| 51 | - | logger.Info(). | |||
| 52 | - | Str("cookie_policy", string(wkOpts.CookiePolicy)). | |||
| 53 | - | Bool("itp_enabled", wkOpts.ITPEnabled). | |||
| 54 | - | Msg("webkit privacy configuration") | |||
| 55 | - | wkCtx, err := webkit.NewWebKitContextWithOptions(ctx, wkOpts) | |||
| 56 | - | if err != nil { | |||
| 57 | - | logger.Fatal().Err(err).Msg("failed to initialize WebKit context") | |||
| 58 | - | } | |||
| 59 | - | logging.Trace().Mark("webkit_context") | |||
| 60 | - | ||||
| 61 | - | filterManager := initFilterManager(ctx, cfg, input.DataDir, logger) | |||
| 62 | - | ||||
| 63 | - | schemeHandler := webkit.NewDumbSchemeHandler(ctx) | |||
| 64 | - | schemeHandler.SetAssets(assets.WebUIAssets) | |||
| 65 | - | schemeHandler.RegisterWithContext(wkCtx) | |||
| 66 | - | ||||
| 67 | - | settings, injector, messageRouter := initWebKitComponents(ctx, cfg, input.ThemeManager, input.ColorResolver, logger) | |||
| 68 | - | ||||
| 69 | - | pool := initWebViewPool(ctx, wkCtx, settings, injector, messageRouter, input.ThemeManager, filterManager, &perfSettings, logger) | |||
| 70 | - | ||||
| 71 | - | return WebKitStack{ | |||
| 72 | - | Context: wkCtx, | |||
| 73 | - | Settings: settings, | |||
| 74 | - | Injector: injector, | |||
| 75 | - | MessageRouter: messageRouter, | |||
| 76 | - | Pool: pool, | |||
| 77 | - | FilterManager: filterManager, | |||
| 78 | - | } | |||
| 79 | - | } | |||
| 80 | - | ||||
| 81 | - | func surveyHardwareAndResolveProfile(ctx context.Context, cfg *config.Config, logger zerolog.Logger) config.ResolvedPerformanceSettings { | |||
| 82 | - | hwSurveyor := env.NewHardwareSurveyor() | |||
| 83 | - | hwInfo := hwSurveyor.Survey(ctx) | |||
| 84 | - | logging.Trace().Mark("hardware_survey") | |||
| 85 | - | logger.Info(). | |||
| 86 | - | Int("cpu_cores", hwInfo.CPUCores). | |||
| 87 | - | Int("cpu_threads", hwInfo.CPUThreads). | |||
| 88 | - | Uint64("ram_mb", hwInfo.TotalRAM/(1024*1024)). | |||
| 89 | - | Str("gpu_vendor", string(hwInfo.GPUVendor)). | |||
| 90 | - | Uint64("vram_mb", hwInfo.VRAM/(1024*1024)). | |||
| 91 | - | Msg("hardware survey completed") | |||
| 92 | - | ||||
| 93 | - | perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo) | |||
| 94 | - | logging.Trace().Mark("performance_profile") | |||
| 95 | - | logger.Info(). | |||
| 96 | - | Str("profile", string(cfg.Performance.Profile)). | |||
| 97 | - | Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads). | |||
| 98 | - | Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads). | |||
| 99 | - | Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount). | |||
| 100 | - | Msg("resolved performance profile") | |||
| 101 | - | ||||
| 102 | - | return perfSettings | |||
| 103 | - | } | |||
| 104 | - | ||||
| 105 | - | func buildWebKitContextOptions( | |||
| 106 | - | cfg *config.Config, | |||
| 107 | - | dataDir, cacheDir string, | |||
| 108 | - | perfSettings *config.ResolvedPerformanceSettings, | |||
| 109 | - | ) port.WebKitContextOptions { | |||
| 110 | - | cookiePolicy := port.WebKitCookiePolicyNoThirdParty | |||
| 111 | - | itpEnabled := true | |||
| 112 | - | if cfg != nil { | |||
| 113 | - | cookiePolicy = port.WebKitCookiePolicy(cfg.Privacy.CookiePolicy) | |||
| 114 | - | itpEnabled = cfg.Privacy.ITPEnabled | |||
| 115 | - | } | |||
| 116 | - | ||||
| 117 | - | wkOpts := port.WebKitContextOptions{ | |||
| 118 | - | DataDir: dataDir, | |||
| 119 | - | CacheDir: cacheDir, | |||
| 120 | - | CookiePolicy: cookiePolicy, | |||
| 121 | - | ITPEnabled: itpEnabled, | |||
| 122 | - | } | |||
| 123 | - | ||||
| 124 | - | if hasWebProcessMemoryConfig(perfSettings) { | |||
| 125 | - | wkOpts.WebProcessMemory = &port.MemoryPressureConfig{ | |||
| 126 | - | MemoryLimitMB: perfSettings.WebProcessMemoryLimitMB, | |||
| 127 | - | PollIntervalSec: perfSettings.WebProcessMemoryPollIntervalSec, | |||
| 128 | - | ConservativeThreshold: perfSettings.WebProcessMemoryConservativeThreshold, | |||
| 129 | - | StrictThreshold: perfSettings.WebProcessMemoryStrictThreshold, | |||
| 130 | - | } | |||
| 131 | - | } | |||
| 132 | - | ||||
| 133 | - | if hasNetworkProcessMemoryConfig(perfSettings) { | |||
| 134 | - | wkOpts.NetworkProcessMemory = &port.MemoryPressureConfig{ | |||
| 135 | - | MemoryLimitMB: perfSettings.NetworkProcessMemoryLimitMB, | |||
| 136 | - | PollIntervalSec: perfSettings.NetworkProcessMemoryPollIntervalSec, | |||
| 137 | - | ConservativeThreshold: perfSettings.NetworkProcessMemoryConservativeThreshold, | |||
| 138 | - | StrictThreshold: perfSettings.NetworkProcessMemoryStrictThreshold, | |||
| 139 | - | } | |||
| 140 | - | } | |||
| 141 | - | ||||
| 142 | - | return wkOpts | |||
| 143 | - | } | |||
| 144 | - | ||||
| 145 | - | func initFilterManager(ctx context.Context, cfg *config.Config, dataDir string, logger zerolog.Logger) *filtering.Manager { | |||
| 146 | - | filterStoreDir := filepath.Join(dataDir, "filters", "store") | |||
| 147 | - | filterJSONDir := filepath.Join(dataDir, "filters", "json") | |||
| 148 | - | filterManager, err := filtering.NewManager(filtering.ManagerConfig{ | |||
| 149 | - | StoreDir: filterStoreDir, | |||
| 150 | - | JSONDir: filterJSONDir, | |||
| 151 | - | Enabled: cfg.ContentFiltering.Enabled, | |||
| 152 | - | AutoUpdate: cfg.ContentFiltering.AutoUpdate, | |||
| 153 | - | }) | |||
| 154 | - | if err != nil { | |||
| 155 | - | logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering") | |||
| 156 | - | return nil | |||
| 157 | - | } | |||
| 158 | - | if err := filterManager.Initialize(ctx); err != nil { | |||
| 159 | - | logger.Warn().Err(err).Msg("failed to initialize filters, will load async") | |||
| 160 | - | } | |||
| 161 | - | logging.Trace().Mark("filter_manager") | |||
| 162 | - | return filterManager | |||
| 163 | - | } | |||
| 164 | - | ||||
| 165 | - | func initWebKitComponents( | |||
| 166 | - | ctx context.Context, | |||
| 167 | - | cfg *config.Config, | |||
| 168 | - | themeManager *theme.Manager, | |||
| 169 | - | colorResolver port.ColorSchemeResolver, | |||
| 170 | - | logger zerolog.Logger, | |||
| 171 | - | ) (*webkit.SettingsManager, *webkit.ContentInjector, *webkit.MessageRouter) { | |||
| 172 | - | settings := webkit.NewSettingsManager(ctx, cfg) | |||
| 173 | - | injector := webkit.NewContentInjector(colorResolver) | |||
| 174 | - | ||||
| 175 | - | injector.SetAutoCopyConfigGetter(func() bool { | |||
| 176 | - | return config.Get().Clipboard.AutoCopyOnSelection | |||
| 177 | - | }) | |||
| 178 | - | ||||
| 179 | - | prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector) | |||
| 180 | - | themeCSSText := themeManager.GetWebUIThemeCSS() | |||
| 181 | - | if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil { | |||
| 182 | - | logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS") | |||
| 183 | - | } | |||
| 184 | - | ||||
| 185 | - | messageRouter := webkit.NewMessageRouter(ctx) | |||
| 186 | - | logging.Trace().Mark("settings_manager") | |||
| 187 | - | ||||
| 188 | - | return settings, injector, messageRouter | |||
| 189 | - | } | |||
| 190 | - | ||||
| 191 | - | func initWebViewPool( | |||
| 192 | - | ctx context.Context, | |||
| 193 | - | wkCtx *webkit.WebKitContext, | |||
| 194 | - | settings *webkit.SettingsManager, | |||
| 195 | - | injector *webkit.ContentInjector, | |||
| 196 | - | messageRouter *webkit.MessageRouter, | |||
| 197 | - | themeManager *theme.Manager, | |||
| 198 | - | filterManager *filtering.Manager, | |||
| 199 | - | perfSettings *config.ResolvedPerformanceSettings, | |||
| 200 | - | logger zerolog.Logger, | |||
| 201 | - | ) *webkit.WebViewPool { | |||
| 202 | - | poolCfg := webkit.DefaultPoolConfig() | |||
| 203 | - | if perfSettings.WebViewPoolPrewarmCount > 0 { | |||
| 204 | - | poolCfg.PrewarmCount = perfSettings.WebViewPoolPrewarmCount | |||
| 205 | - | } | |||
| 206 | - | pool := webkit.NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter) | |||
| 207 | - | ||||
| 208 | - | bgR, bgG, bgB, bgA := themeManager.GetBackgroundRGBA() | |||
| 209 | - | pool.SetBackgroundColor(bgR, bgG, bgB, bgA) | |||
| 210 | - | ||||
| 211 | - | if filterManager != nil { | |||
| 212 | - | pool.SetFilterApplier(filterManager) | |||
| 213 | - | } | |||
| 214 | - | ||||
| 215 | - | if shouldPrewarmFirstWebView(gdk.DisplayGetDefault()) { | |||
| 216 | - | if err := pool.PrewarmFirst(ctx); err != nil { | |||
| 217 | - | logger.Warn().Err(err).Msg("failed to prewarm first webview, first tab may be slower") | |||
| 218 | - | } | |||
| 219 | - | } else { | |||
| 220 | - | logger.Warn().Msg("skipping first webview prewarm: no GDK display available yet") | |||
| 221 | - | } | |||
| 222 | - | logging.Trace().Mark("pool_prewarm_first") | |||
| 223 | - | ||||
| 224 | - | return pool | |||
| 225 | - | } | |||
| 226 | - | ||||
| 227 | - | func shouldPrewarmFirstWebView(display *gdk.Display) bool { | |||
| 228 | - | return display != nil | |||
| 229 | - | } | |||
| 230 | - | ||||
| 231 | - | // hasWebProcessMemoryConfig returns true if any web process memory setting is configured. | |||
| 232 | - | func hasWebProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool { | |||
| 233 | - | return p.WebProcessMemoryLimitMB > 0 || | |||
| 234 | - | p.WebProcessMemoryPollIntervalSec > 0 || | |||
| 235 | - | p.WebProcessMemoryConservativeThreshold > 0 || | |||
| 236 | - | p.WebProcessMemoryStrictThreshold > 0 | |||
| 237 | - | } | |||
| 238 | - | ||||
| 239 | - | // hasNetworkProcessMemoryConfig returns true if any network process memory setting is configured. | |||
| 240 | - | func hasNetworkProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool { | |||
| 241 | - | return p.NetworkProcessMemoryLimitMB > 0 || | |||
| 242 | - | p.NetworkProcessMemoryPollIntervalSec > 0 || | |||
| 243 | - | p.NetworkProcessMemoryConservativeThreshold > 0 || | |||
| 244 | - | p.NetworkProcessMemoryStrictThreshold > 0 | |||
| 245 | - | } | |||
| 246 | - | ||||
| 247 | - | // configureRenderingEnvironment sets up the rendering environment before WebKit/GTK initialization. | |||
| 248 | - | // Environment variables must be set before GTK/WebKit/GStreamer initializes. | |||
| 249 | - | func configureRenderingEnvironment( | |||
| 250 | - | ctx context.Context, | |||
| 251 | - | cfg *config.Config, | |||
| 252 | - | perfSettings *config.ResolvedPerformanceSettings, | |||
| 253 | - | logger zerolog.Logger, | |||
| 254 | - | ) { | |||
| 255 | - | // Install GLib log handler FIRST if configured (must be before any GTK/GLib calls) | |||
| 256 | - | if cfg.Logging.CaptureGTKLogs { | |||
| 257 | - | enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace" | |||
| 258 | - | logging.InstallGLibLogHandler(ctx, logger, enableDebug) | |||
| 259 | - | } | |||
| 260 | - | ||||
| 261 | - | renderEnv := env.NewManager() | |||
| 262 | - | gpuVendor := renderEnv.DetectGPUVendor(ctx) | |||
| 263 | - | renderSettings := env.RenderingSettings{ | |||
| 264 | - | // GStreamer settings | |||
| 265 | - | ForceVSync: cfg.Media.ForceVSync, | |||
| 266 | - | GLRenderingMode: string(cfg.Media.GLRenderingMode), | |||
| 267 | - | GStreamerDebugLevel: cfg.Media.GStreamerDebugLevel, | |||
| 268 | - | ||||
| 269 | - | // WebKit compositor settings | |||
| 270 | - | DisableDMABufRenderer: cfg.Rendering.DisableDMABufRenderer, | |||
| 271 | - | ForceCompositingMode: cfg.Rendering.ForceCompositingMode, | |||
| 272 | - | DisableCompositingMode: cfg.Rendering.DisableCompositingMode, | |||
| 273 | - | ||||
| 274 | - | // GTK/GSK settings | |||
| 275 | - | GSKRenderer: string(cfg.Rendering.GSKRenderer), | |||
| 276 | - | DisableMipmaps: cfg.Rendering.DisableMipmaps, | |||
| 277 | - | PreferGL: cfg.Rendering.PreferGL, | |||
| 278 | - | ||||
| 279 | - | // Debug settings | |||
| 280 | - | ShowFPS: cfg.Rendering.ShowFPS, | |||
| 281 | - | SampleMemory: cfg.Rendering.SampleMemory, | |||
| 282 | - | DebugFrames: cfg.Rendering.DebugFrames, | |||
| 283 | - | ||||
| 284 | - | // Skia rendering thread settings (from resolved profile) | |||
| 285 | - | SkiaCPUPaintingThreads: perfSettings.SkiaCPUPaintingThreads, | |||
| 286 | - | SkiaGPUPaintingThreads: perfSettings.SkiaGPUPaintingThreads, | |||
| 287 | - | SkiaEnableCPURendering: perfSettings.SkiaEnableCPURendering, | |||
| 288 | - | } | |||
| 289 | - | if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil { | |||
| 290 | - | logger.Warn().Err(err).Msg("failed to apply rendering environment") | |||
| 291 | - | } | |||
| 292 | - | logger.Info(). | |||
| 293 | - | Str("gpu", string(gpuVendor)). | |||
| 294 | - | Interface("vars", renderEnv.GetAppliedVars()). | |||
| 295 | - | Msg("rendering environment configured") | |||
| 296 | - | } | |||
M internal/infrastructure/webkit/engine.go +10 -0
| @@ -83,3 +83,13 @@ func (e *Engine) FilterApplier() port.FilterApplier { | |||
| 83 | 83 | func (e *Engine) FaviconDatabase() port.FaviconDatabase { | |
| 84 | 84 | return &faviconDatabaseAdapter{wkCtx: e.wkCtx} | |
| 85 | 85 | } | |
| 86 | + | ||
| 87 | + | // --- Temporary accessors for migration (M3->M4 bridge) --- | |
| 88 | + | // These will be removed when ui.Dependencies is updated to use port.Engine. | |
| 89 | + | ||
| 90 | + | func (e *Engine) InternalContext() *WebKitContext { return e.wkCtx } | |
| 91 | + | func (e *Engine) InternalSettings() *SettingsManager { return e.settings } | |
| 92 | + | func (e *Engine) InternalInjector() *ContentInjector { return e.injector } | |
| 93 | + | func (e *Engine) InternalMessageRouter() *MessageRouter { return e.messageRouter } | |
| 94 | + | func (e *Engine) InternalPool() *WebViewPool { return e.pool } | |
| 95 | + | func (e *Engine) InternalFilterManager() *filtering.Manager { return e.filterManager } | |
| @@ -83,3 +83,13 @@ func (e *Engine) FilterApplier() port.FilterApplier { | |||||
| 83 | func (e *Engine) FaviconDatabase() port.FaviconDatabase { | 83 | func (e *Engine) FaviconDatabase() port.FaviconDatabase { | ||
| 84 | return &faviconDatabaseAdapter{wkCtx: e.wkCtx} | 84 | return &faviconDatabaseAdapter{wkCtx: e.wkCtx} | ||
| 85 | } | 85 | } | ||
| 86 | + | ||||
| 87 | + | // --- Temporary accessors for migration (M3->M4 bridge) --- | |||
| 88 | + | // These will be removed when ui.Dependencies is updated to use port.Engine. | |||
| 89 | + | ||||
| 90 | + | func (e *Engine) InternalContext() *WebKitContext { return e.wkCtx } | |||
| 91 | + | func (e *Engine) InternalSettings() *SettingsManager { return e.settings } | |||
| 92 | + | func (e *Engine) InternalInjector() *ContentInjector { return e.injector } | |||
| 93 | + | func (e *Engine) InternalMessageRouter() *MessageRouter { return e.messageRouter } | |||
| 94 | + | func (e *Engine) InternalPool() *WebViewPool { return e.pool } | |||
| 95 | + | func (e *Engine) InternalFilterManager() *filtering.Manager { return e.filterManager } | |||