1package bootstrap
2
3import (
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/ui/theme"
15 "github.com/rs/zerolog"
16)
17
18type WebKitStack struct {
19 Context *webkit.WebKitContext
20 Settings *webkit.SettingsManager
21 Injector *webkit.ContentInjector
22 MessageRouter *webkit.MessageRouter
23 Pool *webkit.WebViewPool
24 FilterManager *filtering.Manager
25}
26
27func BuildWebKitStack(
28 ctx context.Context,
29 cfg *config.Config,
30 dataDir string,
31 cacheDir string,
32 themeManager *theme.Manager,
33 logger zerolog.Logger,
34) WebKitStack {
35 // CRITICAL: Configure rendering environment BEFORE WebKit/GTK initialization.
36 // Environment variables must be set before GTK/WebKit/GStreamer initializes.
37 renderEnv := env.NewManager()
38 gpuVendor := renderEnv.DetectGPUVendor(ctx)
39 renderSettings := port.RenderingEnvSettings{
40 // GStreamer settings
41 ForceVSync: cfg.Media.ForceVSync,
42 GLRenderingMode: string(cfg.Media.GLRenderingMode),
43 GStreamerDebugLevel: cfg.Media.GStreamerDebugLevel,
44 VideoBufferSizeMB: cfg.Media.VideoBufferSizeMB,
45 QueueBufferTimeSec: cfg.Media.QueueBufferTimeSec,
46
47 // WebKit compositor settings
48 DisableDMABufRenderer: cfg.Rendering.DisableDMABufRenderer,
49 ForceCompositingMode: cfg.Rendering.ForceCompositingMode,
50 DisableCompositingMode: cfg.Rendering.DisableCompositingMode,
51
52 // GTK/GSK settings
53 GSKRenderer: string(cfg.Rendering.GSKRenderer),
54 DisableMipmaps: cfg.Rendering.DisableMipmaps,
55 PreferGL: cfg.Rendering.PreferGL,
56
57 // Debug settings
58 ShowFPS: cfg.Rendering.ShowFPS,
59 SampleMemory: cfg.Rendering.SampleMemory,
60 DebugFrames: cfg.Rendering.DebugFrames,
61 }
62 if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil {
63 logger.Warn().Err(err).Msg("failed to apply rendering environment")
64 }
65 logger.Info().
66 Str("gpu", string(gpuVendor)).
67 Interface("vars", renderEnv.GetAppliedVars()).
68 Msg("rendering environment configured")
69
70 wkCtx, err := webkit.NewWebKitContext(ctx, dataDir, cacheDir)
71 if err != nil {
72 logger.Fatal().Err(err).Msg("failed to initialize WebKit context")
73 }
74
75 filterStoreDir := filepath.Join(dataDir, "filters", "store")
76 filterJSONDir := filepath.Join(dataDir, "filters", "json")
77 filterManager, err := filtering.NewManager(filtering.ManagerConfig{
78 StoreDir: filterStoreDir,
79 JSONDir: filterJSONDir,
80 Enabled: cfg.ContentFiltering.Enabled,
81 AutoUpdate: cfg.ContentFiltering.AutoUpdate,
82 })
83 if err != nil {
84 logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering")
85 } else {
86 if err := filterManager.Initialize(ctx); err != nil {
87 logger.Warn().Err(err).Msg("failed to initialize filters, will load async")
88 }
89 }
90
91 schemeHandler := webkit.NewDumbSchemeHandler(ctx)
92 schemeHandler.SetAssets(assets.WebUIAssets)
93 schemeHandler.RegisterWithContext(wkCtx)
94
95 settings := webkit.NewSettingsManager(ctx, cfg)
96 injector := webkit.NewContentInjector(themeManager.PrefersDark())
97
98 prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
99 themeCSSText := themeManager.GetWebUIThemeCSS()
100 if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil {
101 logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS")
102 }
103
104 messageRouter := webkit.NewMessageRouter(ctx)
105 poolCfg := webkit.DefaultPoolConfig()
106 // Override prewarm count from config if set
107 if cfg.Performance.WebViewPoolPrewarmCount > 0 {
108 poolCfg.PrewarmCount = cfg.Performance.WebViewPoolPrewarmCount
109 }
110 pool := webkit.NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter)
111
112 if filterManager != nil {
113 pool.SetFilterApplier(filterManager)
114 }
115
116 return WebKitStack{
117 Context: wkCtx,
118 Settings: settings,
119 Injector: injector,
120 MessageRouter: messageRouter,
121 Pool: pool,
122 FilterManager: filterManager,
123 }
124}