v0.20.0 c741a39

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close