detached 8987eec

feat(webkit): apply content filters to webviews on creation and acquire

commit
8987eeccfd5ac3a89d8458f797ef80b6f5a70389
author
brice · · 2025-12-18 18:21 UTC
committer
brice · · 2025-12-18 19:27 UTC
parent
92431e4
signature
unsigned
2 files · +52 -8
M internal/infrastructure/webkit/factory.go +26 -5
@@ -12,11 +12,12 @@ import (
1212 // It supports creating regular WebViews (optionally via pool) and
1313 // related WebViews that share session/cookies with a parent (for popups).
1414 type WebViewFactory struct {
15- wkCtx *WebKitContext
16- settings *SettingsManager
17- pool *WebViewPool
18- injector *ContentInjector
19- router *MessageRouter
15+ wkCtx *WebKitContext
16+ settings *SettingsManager
17+ pool *WebViewPool
18+ injector *ContentInjector
19+ router *MessageRouter
20+ filterApplier FilterApplier // Optional content filter applier
2021 }
2122
2223 // NewWebViewFactory creates a new WebViewFactory.
@@ -37,6 +38,16 @@ func NewWebViewFactory(
3738 }
3839 }
3940
41+// SetFilterApplier sets the content filter applier.
42+// Filters will be applied to all newly created WebViews.
43+func (f *WebViewFactory) SetFilterApplier(applier FilterApplier) {
44+ f.filterApplier = applier
45+ // Also propagate to the pool if present
46+ if f.pool != nil {
47+ f.pool.SetFilterApplier(applier)
48+ }
49+}
50+
4051 // Create creates a new WebView instance.
4152 // If a pool is configured, it will try to acquire from the pool first.
4253 func (f *WebViewFactory) Create(ctx context.Context) (*WebView, error) {
@@ -85,6 +96,11 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
8596 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to related webview")
8697 }
8798
99+ // Apply content filters if configured
100+ if f.filterApplier != nil {
101+ f.filterApplier.ApplyTo(ctx, wv.ucm)
102+ }
103+
88104 log.Debug().
89105 Uint64("id", uint64(wv.ID())).
90106 Uint64("parent_id", uint64(parentID)).
@@ -110,6 +126,11 @@ func (f *WebViewFactory) createDirect(ctx context.Context) (*WebView, error) {
110126 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to webview")
111127 }
112128
129+ // Apply content filters if configured
130+ if f.filterApplier != nil {
131+ f.filterApplier.ApplyTo(ctx, wv.ucm)
132+ }
133+
113134 log.Debug().Uint64("id", uint64(wv.ID())).Msg("created webview directly")
114135 return wv, nil
115136 }
@@ -12,11 +12,12 @@ import (
12 // It supports creating regular WebViews (optionally via pool) and12 // It supports creating regular WebViews (optionally via pool) and
13 // related WebViews that share session/cookies with a parent (for popups).13 // related WebViews that share session/cookies with a parent (for popups).
14 type WebViewFactory struct {14 type WebViewFactory struct {
15- wkCtx *WebKitContext15+ wkCtx *WebKitContext
16- settings *SettingsManager16+ settings *SettingsManager
17- pool *WebViewPool17+ pool *WebViewPool
18- injector *ContentInjector18+ injector *ContentInjector
19- router *MessageRouter19+ router *MessageRouter
20+ filterApplier FilterApplier // Optional content filter applier
20 }21 }
21 22
22 // NewWebViewFactory creates a new WebViewFactory.23 // NewWebViewFactory creates a new WebViewFactory.
@@ -37,6 +38,16 @@ func NewWebViewFactory(
37 }38 }
38 }39 }
39 40
41+// SetFilterApplier sets the content filter applier.
42+// Filters will be applied to all newly created WebViews.
43+func (f *WebViewFactory) SetFilterApplier(applier FilterApplier) {
44+ f.filterApplier = applier
45+ // Also propagate to the pool if present
46+ if f.pool != nil {
47+ f.pool.SetFilterApplier(applier)
48+ }
49+}
50+
40 // Create creates a new WebView instance.51 // Create creates a new WebView instance.
41 // If a pool is configured, it will try to acquire from the pool first.52 // If a pool is configured, it will try to acquire from the pool first.
42 func (f *WebViewFactory) Create(ctx context.Context) (*WebView, error) {53 func (f *WebViewFactory) Create(ctx context.Context) (*WebView, error) {
@@ -85,6 +96,11 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
85 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to related webview")96 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to related webview")
86 }97 }
87 98
99+ // Apply content filters if configured
100+ if f.filterApplier != nil {
101+ f.filterApplier.ApplyTo(ctx, wv.ucm)
102+ }
103+
88 log.Debug().104 log.Debug().
89 Uint64("id", uint64(wv.ID())).105 Uint64("id", uint64(wv.ID())).
90 Uint64("parent_id", uint64(parentID)).106 Uint64("parent_id", uint64(parentID)).
@@ -110,6 +126,11 @@ func (f *WebViewFactory) createDirect(ctx context.Context) (*WebView, error) {
110 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to webview")126 log.Warn().Err(err).Uint64("id", uint64(wv.ID())).Msg("failed to attach frontend to webview")
111 }127 }
112 128
129+ // Apply content filters if configured
130+ if f.filterApplier != nil {
131+ f.filterApplier.ApplyTo(ctx, wv.ucm)
132+ }
133+
113 log.Debug().Uint64("id", uint64(wv.ID())).Msg("created webview directly")134 log.Debug().Uint64("id", uint64(wv.ID())).Msg("created webview directly")
114 return wv, nil135 return wv, nil
115 }136 }
M internal/infrastructure/webkit/pool.go +26 -3
@@ -7,8 +7,15 @@ import (
77 "time"
88
99 "github.com/bnema/dumber/internal/logging"
10+ "github.com/bnema/puregotk-webkit/webkit"
1011 )
1112
13+// FilterApplier applies content filters to a UserContentManager.
14+// This interface decouples the pool from the filtering package.
15+type FilterApplier interface {
16+ ApplyTo(ctx context.Context, ucm *webkit.UserContentManager)
17+}
18+
1219 // PoolConfig configures the WebView pool behavior.
1320 type PoolConfig struct {
1421 // MinSize is the minimum number of warm WebViews to maintain.
@@ -37,9 +44,10 @@ type WebViewPool struct {
3744 settings *SettingsManager
3845 config PoolConfig
3946
40- pool chan *WebView
41- injector *ContentInjector
42- router *MessageRouter
47+ pool chan *WebView
48+ injector *ContentInjector
49+ router *MessageRouter
50+ filterApplier FilterApplier // Optional content filter applier
4351
4452 closed atomic.Bool
4553 wg sync.WaitGroup
@@ -75,6 +83,12 @@ func NewWebViewPool(ctx context.Context, wkCtx *WebKitContext, settings *Setting
7583 return p
7684 }
7785
86+// SetFilterApplier sets the content filter applier.
87+// Filters will be applied to all newly created WebViews.
88+func (p *WebViewPool) SetFilterApplier(applier FilterApplier) {
89+ p.filterApplier = applier
90+}
91+
7892 // Acquire gets a WebView from the pool or creates a new one.
7993 func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
8094 log := logging.FromContext(ctx)
@@ -89,6 +103,10 @@ func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
89103 if wv != nil && !wv.IsDestroyed() {
90104 // Ensure frontend is attached even if this WebView was pooled before injection was configured.
91105 _ = wv.AttachFrontend(ctx, p.injector, p.router)
106+ // Apply filters if available (may not have been applied during prewarm)
107+ if p.filterApplier != nil {
108+ p.filterApplier.ApplyTo(ctx, wv.ucm)
109+ }
92110 log.Debug().Uint64("id", uint64(wv.ID())).Msg("acquired webview from pool")
93111 return wv, nil
94112 }
@@ -117,6 +135,11 @@ func (p *WebViewPool) createWebView(ctx context.Context) (*WebView, error) {
117135 log.Warn().Err(err).Msg("failed to attach frontend to new webview")
118136 }
119137
138+ // Apply content filters if configured
139+ if p.filterApplier != nil {
140+ p.filterApplier.ApplyTo(ctx, wv.ucm)
141+ }
142+
120143 return wv, nil
121144 }
122145
@@ -7,8 +7,15 @@ import (
7 "time"7 "time"
8 8
9 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
10+ "github.com/bnema/puregotk-webkit/webkit"
10 )11 )
11 12
13+// FilterApplier applies content filters to a UserContentManager.
14+// This interface decouples the pool from the filtering package.
15+type FilterApplier interface {
16+ ApplyTo(ctx context.Context, ucm *webkit.UserContentManager)
17+}
18+
12 // PoolConfig configures the WebView pool behavior.19 // PoolConfig configures the WebView pool behavior.
13 type PoolConfig struct {20 type PoolConfig struct {
14 // MinSize is the minimum number of warm WebViews to maintain.21 // MinSize is the minimum number of warm WebViews to maintain.
@@ -37,9 +44,10 @@ type WebViewPool struct {
37 settings *SettingsManager44 settings *SettingsManager
38 config PoolConfig45 config PoolConfig
39 46
40- pool chan *WebView47+ pool chan *WebView
41- injector *ContentInjector48+ injector *ContentInjector
42- router *MessageRouter49+ router *MessageRouter
50+ filterApplier FilterApplier // Optional content filter applier
43 51
44 closed atomic.Bool52 closed atomic.Bool
45 wg sync.WaitGroup53 wg sync.WaitGroup
@@ -75,6 +83,12 @@ func NewWebViewPool(ctx context.Context, wkCtx *WebKitContext, settings *Setting
75 return p83 return p
76 }84 }
77 85
86+// SetFilterApplier sets the content filter applier.
87+// Filters will be applied to all newly created WebViews.
88+func (p *WebViewPool) SetFilterApplier(applier FilterApplier) {
89+ p.filterApplier = applier
90+}
91+
78 // Acquire gets a WebView from the pool or creates a new one.92 // Acquire gets a WebView from the pool or creates a new one.
79 func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {93 func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
80 log := logging.FromContext(ctx)94 log := logging.FromContext(ctx)
@@ -89,6 +103,10 @@ func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
89 if wv != nil && !wv.IsDestroyed() {103 if wv != nil && !wv.IsDestroyed() {
90 // Ensure frontend is attached even if this WebView was pooled before injection was configured.104 // Ensure frontend is attached even if this WebView was pooled before injection was configured.
91 _ = wv.AttachFrontend(ctx, p.injector, p.router)105 _ = wv.AttachFrontend(ctx, p.injector, p.router)
106+ // Apply filters if available (may not have been applied during prewarm)
107+ if p.filterApplier != nil {
108+ p.filterApplier.ApplyTo(ctx, wv.ucm)
109+ }
92 log.Debug().Uint64("id", uint64(wv.ID())).Msg("acquired webview from pool")110 log.Debug().Uint64("id", uint64(wv.ID())).Msg("acquired webview from pool")
93 return wv, nil111 return wv, nil
94 }112 }
@@ -117,6 +135,11 @@ func (p *WebViewPool) createWebView(ctx context.Context) (*WebView, error) {
117 log.Warn().Err(err).Msg("failed to attach frontend to new webview")135 log.Warn().Err(err).Msg("failed to attach frontend to new webview")
118 }136 }
119 137
138+ // Apply content filters if configured
139+ if p.filterApplier != nil {
140+ p.filterApplier.ApplyTo(ctx, wv.ucm)
141+ }
142+
120 return wv, nil143 return wv, nil
121 }144 }
122 145

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