context.go RawHistoryFolder
1package webkit
2
3import (
4 "context"
5 "fmt"
6 "path/filepath"
7 "sync"
8
9 "github.com/bnema/dumber/internal/logging"
10 "github.com/bnema/puregotk-webkit/webkit"
11 "github.com/rs/zerolog"
12)
13
14// FaviconDatabase is a type alias for webkit.FaviconDatabase.
15// Re-exported for use by UI layer without direct puregotk-webkit import.
16type FaviconDatabase = webkit.FaviconDatabase
17
18// WebKitContext manages the shared WebContext and persistent NetworkSession.
19// IMPORTANT: This MUST be initialized before creating any WebViews.
20type WebKitContext struct {
21 webContext *webkit.WebContext
22 networkSession *webkit.NetworkSession
23 faviconDB *webkit.FaviconDatabase
24
25 dataDir string
26 cacheDir string
27
28 logger zerolog.Logger
29 mu sync.RWMutex
30 initialized bool
31}
32
33// NewWebKitContext creates and initializes a WebKitContext with a persistent NetworkSession.
34// The dataDir and cacheDir are used for cookie storage, cache, and other persistent data.
35// This MUST be called before creating any WebViews to ensure they use persistent storage.
36func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) {
37 log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger()
38
39 if dataDir == "" {
40 return nil, fmt.Errorf("data directory cannot be empty")
41 }
42 if cacheDir == "" {
43 return nil, fmt.Errorf("cache directory cannot be empty")
44 }
45
46 wkCtx := &WebKitContext{
47 dataDir: dataDir,
48 cacheDir: cacheDir,
49 logger: log,
50 }
51
52 // Create persistent network session FIRST
53 // Per WebKitGTK 6.0 docs: "The first WebKitNetworkSession created becomes the default"
54 if err := wkCtx.initNetworkSession(); err != nil {
55 return nil, fmt.Errorf("failed to init network session: %w", err)
56 }
57
58 // Get or create WebContext
59 wkCtx.webContext = webkit.WebContextGetDefault()
60 if wkCtx.webContext == nil {
61 wkCtx.webContext = webkit.NewWebContext()
62 }
63 if wkCtx.webContext == nil {
64 return nil, fmt.Errorf("failed to get or create WebContext")
65 }
66
67 // Set cache model for browser-style caching
68 wkCtx.webContext.SetCacheModel(webkit.CacheModelWebBrowserValue)
69
70 wkCtx.initialized = true
71 log.Info().
72 Str("data_dir", dataDir).
73 Str("cache_dir", cacheDir).
74 Msg("webkit context initialized")
75
76 return wkCtx, nil
77}
78
79// initNetworkSession creates and configures the persistent network session.
80func (c *WebKitContext) initNetworkSession() error {
81 // Create persistent network session
82 session := webkit.NewNetworkSession(&c.dataDir, &c.cacheDir)
83 if session == nil {
84 return fmt.Errorf("failed to create network session")
85 }
86
87 // Verify session is not ephemeral
88 if session.IsEphemeral() {
89 return fmt.Errorf("network session is ephemeral despite providing data directories")
90 }
91
92 // Verify WebsiteDataManager is not ephemeral
93 dataManager := session.GetWebsiteDataManager()
94 if dataManager == nil {
95 return fmt.Errorf("failed to get website data manager")
96 }
97 if dataManager.IsEphemeral() {
98 return fmt.Errorf("website data manager is ephemeral")
99 }
100
101 // Debug: verify the directories WebKit is actually using
102 actualDataDir := dataManager.GetBaseDataDirectory()
103 actualCacheDir := dataManager.GetBaseCacheDirectory()
104 c.logger.Info().
105 Str("expected_data_dir", c.dataDir).
106 Str("actual_data_dir", actualDataDir).
107 Str("expected_cache_dir", c.cacheDir).
108 Str("actual_cache_dir", actualCacheDir).
109 Msg("webkit directories configured")
110
111 // Enable favicon collection and get database reference
112 dataManager.SetFaviconsEnabled(true)
113 c.faviconDB = dataManager.GetFaviconDatabase()
114 if c.faviconDB != nil {
115 c.logger.Debug().Msg("favicon database enabled")
116 }
117
118 // Configure cookie storage
119 cookieManager := session.GetCookieManager()
120 if cookieManager == nil {
121 return fmt.Errorf("failed to get cookie manager")
122 }
123
124 cookiePath := filepath.Join(c.dataDir, "cookies.db")
125 cookieManager.SetPersistentStorage(cookiePath, webkit.CookiePersistentStorageSqliteValue)
126 cookieManager.SetAcceptPolicy(webkit.CookiePolicyAcceptNoThirdPartyValue)
127
128 c.logger.Debug().
129 Str("cookie_path", cookiePath).
130 Msg("cookie storage configured")
131
132 // Enable persistent credential storage
133 session.SetPersistentCredentialStorageEnabled(true)
134
135 // Configure TLS errors to emit signals (enables load-failed-with-tls-errors)
136 session.SetTlsErrorsPolicy(webkit.TlsErrorsPolicyFailValue)
137
138 // Verify default session
139 defaultSession := webkit.NetworkSessionGetDefault()
140 if defaultSession == nil || defaultSession.IsEphemeral() {
141 c.logger.Warn().Msg("default session may not be persistent - created session might not be used by default")
142 }
143
144 c.networkSession = session
145 c.logger.Debug().Msg("network session initialized as persistent")
146
147 return nil
148}
149
150// Context returns the shared WebContext.
151func (c *WebKitContext) Context() *webkit.WebContext {
152 c.mu.RLock()
153 defer c.mu.RUnlock()
154 return c.webContext
155}
156
157// NetworkSession returns the persistent NetworkSession.
158func (c *WebKitContext) NetworkSession() *webkit.NetworkSession {
159 c.mu.RLock()
160 defer c.mu.RUnlock()
161 return c.networkSession
162}
163
164// FaviconDatabase returns the favicon database for persistent favicon storage.
165func (c *WebKitContext) FaviconDatabase() *webkit.FaviconDatabase {
166 c.mu.RLock()
167 defer c.mu.RUnlock()
168 return c.faviconDB
169}
170
171// DataDir returns the data directory path.
172func (c *WebKitContext) DataDir() string {
173 return c.dataDir
174}
175
176// CacheDir returns the cache directory path.
177func (c *WebKitContext) CacheDir() string {
178 return c.cacheDir
179}
180
181// IsInitialized returns true if the context has been successfully initialized.
182func (c *WebKitContext) IsInitialized() bool {
183 c.mu.RLock()
184 defer c.mu.RUnlock()
185 return c.initialized
186}
187
188// PrefetchDNS prefetches DNS for the given hostname to speed up future requests.
189func (c *WebKitContext) PrefetchDNS(hostname string) {
190 if hostname == "" || c.networkSession == nil {
191 return
192 }
193 c.networkSession.PrefetchDns(hostname)
194}
195
196// Close performs cleanup. Currently a no-op as WebKit handles cleanup internally.
197func (c *WebKitContext) Close() error {
198 c.mu.Lock()
199 defer c.mu.Unlock()
200
201 c.initialized = false
202 c.logger.Debug().Msg("webkit context closed")
203 return nil
204}