detached c068e17

refactor: complete engine abstraction (M5+M6) — decouple UI from webkit

Port layer:
- Add RunJavaScript, SetBackgroundColor, Favicon, Generation to port.WebView
- Add OnLinkMiddleClick, OnEnterFullscreen, OnLeaveFullscreen, OnAudioStateChanged to WebViewCallbacks
- Add RefreshScripts(ctx, wv) error to ContentInjector
- Add NativeWidgetProvider optional interface for GTK widget embedding
- Add MemoryPressureConfig, MemoryPressureApplier (moved from deleted webkit_context.go)
- Remove Engine.Init() (no-op — construction is initialization)
- Delete port/webkit_context.go (absorbed into engine.go)

Coordinator decoupling (0 webkit imports):
- Use port.WebView methods directly instead of *webkit.WebView type assertions
- Use port.NativeWidgetProvider for GTK widget embedding
- Move fullscreen/audio/middle-click callbacks into WebViewCallbacks
- Use injector.RefreshScripts replacing manual UCM pattern
- Fix data races in ApplyWebUIThemeToAll and RefreshInjectedScriptsToAll (snapshot under lock)

Favicon adapter decoupling (0 webkit imports):
- Replace *webkit.FaviconDatabase with port.FaviconDatabase
- Implement real GetFaviconAsync in webkit adapter (GIO async pattern)

App.go consolidation:
- Remove 5 redundant concrete webkit fields (pool, factory, injector, router, settings)
- Keep single wkEngine escape hatch for webkit-specific operations
- Use engine.Close() for proper shutdown

Config cleanup:
- Remove old Rendering, Performance, Privacy, Runtime sections from Config struct
- Delete RenderingConfig, PrivacyConfig, RuntimeConfig structs
- Redirect all consumers to cfg.Engine / cfg.Engine.WebKit
- Add PerformanceConfigFromEngine helper for profile resolution

Webkit infrastructure:
- Store engine context for proper lifecycle management
- Replace panic in messageRouterAdapter with logged warning
- Fix performance profile reading from zeroed cfg.Performance to cfg.Engine
- Add webkit-local context_options.go (unexported types)
commit
c068e175212f2afb684a96123a6ac43ff9f0ff17
author
brice · · 2026-03-16 21:58 UTC
parent
3432396
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
42 files · +1119 -1149
M .mockery.yaml +0 -1
@@ -20,7 +20,6 @@ packages:
2020 WebViewFactory: {}
2121 WebViewPool: {}
2222 MemoryPressureApplier: {}
23- WebKitContextProvider: {}
2423 HardwareSurveyor: {}
2524 ColorSchemeDetector: {}
2625 ColorSchemeResolver: {}
@@ -20,7 +20,6 @@ packages:
20 WebViewFactory: {}20 WebViewFactory: {}
21 WebViewPool: {}21 WebViewPool: {}
22 MemoryPressureApplier: {}22 MemoryPressureApplier: {}
23- WebKitContextProvider: {}
24 HardwareSurveyor: {}23 HardwareSurveyor: {}
25 ColorSchemeDetector: {}24 ColorSchemeDetector: {}
26 ColorSchemeResolver: {}25 ColorSchemeResolver: {}
M cmd/dumber/main.go +8 -4
@@ -22,6 +22,7 @@ import (
2222 "github.com/bnema/dumber/internal/infrastructure/config"
2323 "github.com/bnema/dumber/internal/infrastructure/deps"
2424 "github.com/bnema/dumber/internal/infrastructure/favicon"
25+ "github.com/bnema/dumber/internal/infrastructure/filtering"
2526 "github.com/bnema/dumber/internal/infrastructure/idle"
2627 "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite"
2728 "github.com/bnema/dumber/internal/infrastructure/updater"
@@ -213,7 +214,7 @@ func buildAndConfigureApp(
213214 }
214215
215216 func initStartupContext(cfg *config.Config) context.Context {
216- deps.ApplyPrefixEnv(cfg.Runtime.Prefix)
217+ deps.ApplyPrefixEnv(cfg.Engine.WebKit.Prefix)
217218 bootstrapLogger := logging.NewFromConfigValuesWithTimeFormat(
218219 cfg.Logging.Level,
219220 cfg.Logging.Format,
@@ -468,7 +469,7 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases {
468469 xdgDirs, _ := config.GetXDGDirs()
469470 stateDir, _ := config.GetStateDir()
470471 defaultZoom := cfg.DefaultWebpageZoom
471- zoomCache := cache.NewLRU[string, *entity.ZoomLevel](cfg.Performance.ZoomCacheSize)
472+ zoomCache := cache.NewLRU[string, *entity.ZoomLevel](cfg.Engine.ZoomCacheSize)
472473
473474 buildInfo := build.Info{
474475 Version: version,
@@ -515,7 +516,10 @@ func buildUIDependencies(
515516 startupCrashReports []string,
516517 ) *ui.Dependencies {
517518 // Type-assert to *webkit.Engine to access FilterManager (not yet on port.Engine).
518- wkEngine := engine.(*webkit.Engine)
519+ var filterManager *filtering.Manager
520+ if wkEngine, ok := engine.(*webkit.Engine); ok {
521+ filterManager = wkEngine.InternalFilterManager()
522+ }
519523
520524 return &ui.Dependencies{
521525 Ctx: ctx,
@@ -528,7 +532,7 @@ func buildUIDependencies(
528532 AdwaitaDetector: adwaitaDetector,
529533 XDG: xdg.New(),
530534 Engine: engine,
531- FilterManager: wkEngine.InternalFilterManager(),
535+ FilterManager: filterManager,
532536 HistoryRepo: repos.history,
533537 FavoriteRepo: repos.favorite,
534538 ZoomRepo: repos.zoom,
@@ -22,6 +22,7 @@ import (
22 "github.com/bnema/dumber/internal/infrastructure/config"22 "github.com/bnema/dumber/internal/infrastructure/config"
23 "github.com/bnema/dumber/internal/infrastructure/deps"23 "github.com/bnema/dumber/internal/infrastructure/deps"
24 "github.com/bnema/dumber/internal/infrastructure/favicon"24 "github.com/bnema/dumber/internal/infrastructure/favicon"
25+ "github.com/bnema/dumber/internal/infrastructure/filtering"
25 "github.com/bnema/dumber/internal/infrastructure/idle"26 "github.com/bnema/dumber/internal/infrastructure/idle"
26 "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite"27 "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite"
27 "github.com/bnema/dumber/internal/infrastructure/updater"28 "github.com/bnema/dumber/internal/infrastructure/updater"
@@ -213,7 +214,7 @@ func buildAndConfigureApp(
213 }214 }
214 215
215 func initStartupContext(cfg *config.Config) context.Context {216 func initStartupContext(cfg *config.Config) context.Context {
216- deps.ApplyPrefixEnv(cfg.Runtime.Prefix)217+ deps.ApplyPrefixEnv(cfg.Engine.WebKit.Prefix)
217 bootstrapLogger := logging.NewFromConfigValuesWithTimeFormat(218 bootstrapLogger := logging.NewFromConfigValuesWithTimeFormat(
218 cfg.Logging.Level,219 cfg.Logging.Level,
219 cfg.Logging.Format,220 cfg.Logging.Format,
@@ -468,7 +469,7 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases {
468 xdgDirs, _ := config.GetXDGDirs()469 xdgDirs, _ := config.GetXDGDirs()
469 stateDir, _ := config.GetStateDir()470 stateDir, _ := config.GetStateDir()
470 defaultZoom := cfg.DefaultWebpageZoom471 defaultZoom := cfg.DefaultWebpageZoom
471- zoomCache := cache.NewLRU[string, *entity.ZoomLevel](cfg.Performance.ZoomCacheSize)472+ zoomCache := cache.NewLRU[string, *entity.ZoomLevel](cfg.Engine.ZoomCacheSize)
472 473
473 buildInfo := build.Info{474 buildInfo := build.Info{
474 Version: version,475 Version: version,
@@ -515,7 +516,10 @@ func buildUIDependencies(
515 startupCrashReports []string,516 startupCrashReports []string,
516 ) *ui.Dependencies {517 ) *ui.Dependencies {
517 // Type-assert to *webkit.Engine to access FilterManager (not yet on port.Engine).518 // Type-assert to *webkit.Engine to access FilterManager (not yet on port.Engine).
518- wkEngine := engine.(*webkit.Engine)519+ var filterManager *filtering.Manager
520+ if wkEngine, ok := engine.(*webkit.Engine); ok {
521+ filterManager = wkEngine.InternalFilterManager()
522+ }
519 523
520 return &ui.Dependencies{524 return &ui.Dependencies{
521 Ctx: ctx,525 Ctx: ctx,
@@ -528,7 +532,7 @@ func buildUIDependencies(
528 AdwaitaDetector: adwaitaDetector,532 AdwaitaDetector: adwaitaDetector,
529 XDG: xdg.New(),533 XDG: xdg.New(),
530 Engine: engine,534 Engine: engine,
531- FilterManager: wkEngine.InternalFilterManager(),535+ FilterManager: filterManager,
532 HistoryRepo: repos.history,536 HistoryRepo: repos.history,
533 FavoriteRepo: repos.favorite,537 FavoriteRepo: repos.favorite,
534 ZoomRepo: repos.zoom,538 ZoomRepo: repos.zoom,
M internal/application/port/content_injector.go +5 -0
@@ -8,4 +8,9 @@ type ContentInjector interface {
88 // InjectThemeCSS injects CSS variables into the page for theming.
99 // The css parameter should contain CSS custom property declarations.
1010 InjectThemeCSS(ctx context.Context, css string) error
11+
12+ // RefreshScripts clears and re-injects user scripts for a single WebView.
13+ // Called when appearance settings change so future navigations pick up latest values.
14+ // Returns an error if the refresh could not be performed.
15+ RefreshScripts(ctx context.Context, wv WebView) error
1116 }
@@ -8,4 +8,9 @@ type ContentInjector interface {
8 // InjectThemeCSS injects CSS variables into the page for theming.8 // InjectThemeCSS injects CSS variables into the page for theming.
9 // The css parameter should contain CSS custom property declarations.9 // The css parameter should contain CSS custom property declarations.
10 InjectThemeCSS(ctx context.Context, css string) error10 InjectThemeCSS(ctx context.Context, css string) error
11+
12+ // RefreshScripts clears and re-injects user scripts for a single WebView.
13+ // Called when appearance settings change so future navigations pick up latest values.
14+ // Returns an error if the refresh could not be performed.
15+ RefreshScripts(ctx context.Context, wv WebView) error
11 }16 }
M internal/application/port/engine.go +57 -4
@@ -17,6 +17,53 @@ const (
1717 CookiePolicyNever CookiePolicy = "never"
1818 )
1919
20+// MemoryPressureConfig holds memory pressure settings for an engine process.
21+// This is a value type (no infrastructure dependencies) co-located with the
22+// interfaces that use it (MemoryPressureApplier, EngineOptions).
23+// Zero values mean "use engine defaults".
24+type MemoryPressureConfig struct {
25+ // MemoryLimitMB sets the memory limit in megabytes.
26+ // 0 means unset (uses engine default).
27+ MemoryLimitMB int
28+
29+ // PollIntervalSec sets the interval for memory usage checks.
30+ // 0 means unset (uses engine default: 30 seconds).
31+ PollIntervalSec float64
32+
33+ // ConservativeThreshold sets threshold for conservative memory release.
34+ // Must be in (0, 1). 0 means unset (uses engine default: 0.33).
35+ ConservativeThreshold float64
36+
37+ // StrictThreshold sets threshold for strict memory release.
38+ // Must be in (0, 1). 0 means unset (uses engine default: 0.5).
39+ StrictThreshold float64
40+}
41+
42+// IsConfigured returns true if any memory pressure setting is configured.
43+func (c *MemoryPressureConfig) IsConfigured() bool {
44+ if c == nil {
45+ return false
46+ }
47+ return c.MemoryLimitMB > 0 ||
48+ c.PollIntervalSec > 0 ||
49+ c.ConservativeThreshold > 0 ||
50+ c.StrictThreshold > 0
51+}
52+
53+// MemoryPressureApplier applies memory pressure settings to engine processes.
54+// This interface abstracts engine-specific memory pressure configuration
55+// to allow testing without engine dependencies.
56+type MemoryPressureApplier interface {
57+ // ApplyNetworkProcessSettings applies memory pressure settings to the network process.
58+ // Must be called BEFORE creating any NetworkSession.
59+ ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error
60+
61+ // ApplyWebProcessSettings applies memory pressure settings to web processes.
62+ // Returns an opaque settings object that should be passed to context creation.
63+ // Returns nil if no settings are configured.
64+ ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error)
65+}
66+
2067 // EngineOptions configures engine initialization.
2168 type EngineOptions struct {
2269 // DataDir is the directory for persistent data (cookies, storage, etc.).
@@ -41,11 +88,10 @@ type EngineOptions struct {
4188 // Engine is the top-level interface for a browser engine implementation.
4289 // It provides access to all engine subsystems and manages the lifecycle
4390 // of the underlying browser context.
91+//
92+// Engines are fully initialized by their constructor (e.g., webkit.NewEngine).
93+// There is no separate Init step.
4494 type Engine interface {
45- // Init initializes the engine with the given options.
46- // Must be called before any other methods.
47- Init(ctx context.Context, opts EngineOptions) error
48-
4995 // Factory returns the WebViewFactory for creating new WebView instances.
5096 Factory() WebViewFactory
5197
@@ -132,3 +178,10 @@ type ScriptRefresher interface {
132178 // RefreshAll re-injects scripts into all provided WebView instances.
133179 RefreshAll(ctx context.Context, webviews []WebView)
134180 }
181+
182+// NativeWidgetProvider is an optional capability for WebViews that can provide
183+// a native GTK widget pointer for embedding into the UI layout.
184+type NativeWidgetProvider interface {
185+ // NativeWidget returns the underlying native widget pointer.
186+ NativeWidget() uintptr
187+}
@@ -17,6 +17,53 @@ const (
17 CookiePolicyNever CookiePolicy = "never"17 CookiePolicyNever CookiePolicy = "never"
18 )18 )
19 19
20+// MemoryPressureConfig holds memory pressure settings for an engine process.
21+// This is a value type (no infrastructure dependencies) co-located with the
22+// interfaces that use it (MemoryPressureApplier, EngineOptions).
23+// Zero values mean "use engine defaults".
24+type MemoryPressureConfig struct {
25+ // MemoryLimitMB sets the memory limit in megabytes.
26+ // 0 means unset (uses engine default).
27+ MemoryLimitMB int
28+
29+ // PollIntervalSec sets the interval for memory usage checks.
30+ // 0 means unset (uses engine default: 30 seconds).
31+ PollIntervalSec float64
32+
33+ // ConservativeThreshold sets threshold for conservative memory release.
34+ // Must be in (0, 1). 0 means unset (uses engine default: 0.33).
35+ ConservativeThreshold float64
36+
37+ // StrictThreshold sets threshold for strict memory release.
38+ // Must be in (0, 1). 0 means unset (uses engine default: 0.5).
39+ StrictThreshold float64
40+}
41+
42+// IsConfigured returns true if any memory pressure setting is configured.
43+func (c *MemoryPressureConfig) IsConfigured() bool {
44+ if c == nil {
45+ return false
46+ }
47+ return c.MemoryLimitMB > 0 ||
48+ c.PollIntervalSec > 0 ||
49+ c.ConservativeThreshold > 0 ||
50+ c.StrictThreshold > 0
51+}
52+
53+// MemoryPressureApplier applies memory pressure settings to engine processes.
54+// This interface abstracts engine-specific memory pressure configuration
55+// to allow testing without engine dependencies.
56+type MemoryPressureApplier interface {
57+ // ApplyNetworkProcessSettings applies memory pressure settings to the network process.
58+ // Must be called BEFORE creating any NetworkSession.
59+ ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error
60+
61+ // ApplyWebProcessSettings applies memory pressure settings to web processes.
62+ // Returns an opaque settings object that should be passed to context creation.
63+ // Returns nil if no settings are configured.
64+ ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error)
65+}
66+
20 // EngineOptions configures engine initialization.67 // EngineOptions configures engine initialization.
21 type EngineOptions struct {68 type EngineOptions struct {
22 // DataDir is the directory for persistent data (cookies, storage, etc.).69 // DataDir is the directory for persistent data (cookies, storage, etc.).
@@ -41,11 +88,10 @@ type EngineOptions struct {
41 // Engine is the top-level interface for a browser engine implementation.88 // Engine is the top-level interface for a browser engine implementation.
42 // It provides access to all engine subsystems and manages the lifecycle89 // It provides access to all engine subsystems and manages the lifecycle
43 // of the underlying browser context.90 // of the underlying browser context.
91+//
92+// Engines are fully initialized by their constructor (e.g., webkit.NewEngine).
93+// There is no separate Init step.
44 type Engine interface {94 type Engine interface {
45- // Init initializes the engine with the given options.
46- // Must be called before any other methods.
47- Init(ctx context.Context, opts EngineOptions) error
48-
49 // Factory returns the WebViewFactory for creating new WebView instances.95 // Factory returns the WebViewFactory for creating new WebView instances.
50 Factory() WebViewFactory96 Factory() WebViewFactory
51 97
@@ -132,3 +178,10 @@ type ScriptRefresher interface {
132 // RefreshAll re-injects scripts into all provided WebView instances.178 // RefreshAll re-injects scripts into all provided WebView instances.
133 RefreshAll(ctx context.Context, webviews []WebView)179 RefreshAll(ctx context.Context, webviews []WebView)
134 }180 }
181+
182+// NativeWidgetProvider is an optional capability for WebViews that can provide
183+// a native GTK widget pointer for embedding into the UI layout.
184+type NativeWidgetProvider interface {
185+ // NativeWidget returns the underlying native widget pointer.
186+ NativeWidget() uintptr
187+}
M internal/application/port/engine_context_test.go +12 -12
@@ -81,27 +81,27 @@ func TestMemoryPressureConfig_IsConfigured(t *testing.T) {
8181 }
8282 }
8383
84-func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) {
84+func TestEngineOptions_IsWebProcessMemoryConfigured(t *testing.T) {
8585 tests := []struct {
8686 name string
87- opts port.WebKitContextOptions
87+ opts port.EngineOptions
8888 expected bool
8989 }{
9090 {
9191 name: "nil WebProcessMemory",
92- opts: port.WebKitContextOptions{},
92+ opts: port.EngineOptions{},
9393 expected: false,
9494 },
9595 {
9696 name: "empty WebProcessMemory",
97- opts: port.WebKitContextOptions{
97+ opts: port.EngineOptions{
9898 WebProcessMemory: &port.MemoryPressureConfig{},
9999 },
100100 expected: false,
101101 },
102102 {
103103 name: "configured WebProcessMemory",
104- opts: port.WebKitContextOptions{
104+ opts: port.EngineOptions{
105105 WebProcessMemory: &port.MemoryPressureConfig{
106106 MemoryLimitMB: 1024,
107107 },
@@ -112,33 +112,33 @@ func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) {
112112
113113 for _, tt := range tests {
114114 t.Run(tt.name, func(t *testing.T) {
115- result := tt.opts.IsWebProcessMemoryConfigured()
115+ result := tt.opts.WebProcessMemory.IsConfigured()
116116 assert.Equal(t, tt.expected, result)
117117 })
118118 }
119119 }
120120
121-func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {
121+func TestEngineOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {
122122 tests := []struct {
123123 name string
124- opts port.WebKitContextOptions
124+ opts port.EngineOptions
125125 expected bool
126126 }{
127127 {
128128 name: "nil NetworkProcessMemory",
129- opts: port.WebKitContextOptions{},
129+ opts: port.EngineOptions{},
130130 expected: false,
131131 },
132132 {
133133 name: "empty NetworkProcessMemory",
134- opts: port.WebKitContextOptions{
134+ opts: port.EngineOptions{
135135 NetworkProcessMemory: &port.MemoryPressureConfig{},
136136 },
137137 expected: false,
138138 },
139139 {
140140 name: "configured NetworkProcessMemory",
141- opts: port.WebKitContextOptions{
141+ opts: port.EngineOptions{
142142 NetworkProcessMemory: &port.MemoryPressureConfig{
143143 PollIntervalSec: 20.0,
144144 },
@@ -149,7 +149,7 @@ func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {
149149
150150 for _, tt := range tests {
151151 t.Run(tt.name, func(t *testing.T) {
152- result := tt.opts.IsNetworkProcessMemoryConfigured()
152+ result := tt.opts.NetworkProcessMemory.IsConfigured()
153153 assert.Equal(t, tt.expected, result)
154154 })
155155 }
@@ -81,27 +81,27 @@ func TestMemoryPressureConfig_IsConfigured(t *testing.T) {
81 }81 }
82 }82 }
83 83
84-func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) {84+func TestEngineOptions_IsWebProcessMemoryConfigured(t *testing.T) {
85 tests := []struct {85 tests := []struct {
86 name string86 name string
87- opts port.WebKitContextOptions87+ opts port.EngineOptions
88 expected bool88 expected bool
89 }{89 }{
90 {90 {
91 name: "nil WebProcessMemory",91 name: "nil WebProcessMemory",
92- opts: port.WebKitContextOptions{},92+ opts: port.EngineOptions{},
93 expected: false,93 expected: false,
94 },94 },
95 {95 {
96 name: "empty WebProcessMemory",96 name: "empty WebProcessMemory",
97- opts: port.WebKitContextOptions{97+ opts: port.EngineOptions{
98 WebProcessMemory: &port.MemoryPressureConfig{},98 WebProcessMemory: &port.MemoryPressureConfig{},
99 },99 },
100 expected: false,100 expected: false,
101 },101 },
102 {102 {
103 name: "configured WebProcessMemory",103 name: "configured WebProcessMemory",
104- opts: port.WebKitContextOptions{104+ opts: port.EngineOptions{
105 WebProcessMemory: &port.MemoryPressureConfig{105 WebProcessMemory: &port.MemoryPressureConfig{
106 MemoryLimitMB: 1024,106 MemoryLimitMB: 1024,
107 },107 },
@@ -112,33 +112,33 @@ func TestWebKitContextOptions_IsWebProcessMemoryConfigured(t *testing.T) {
112 112
113 for _, tt := range tests {113 for _, tt := range tests {
114 t.Run(tt.name, func(t *testing.T) {114 t.Run(tt.name, func(t *testing.T) {
115- result := tt.opts.IsWebProcessMemoryConfigured()115+ result := tt.opts.WebProcessMemory.IsConfigured()
116 assert.Equal(t, tt.expected, result)116 assert.Equal(t, tt.expected, result)
117 })117 })
118 }118 }
119 }119 }
120 120
121-func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {121+func TestEngineOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {
122 tests := []struct {122 tests := []struct {
123 name string123 name string
124- opts port.WebKitContextOptions124+ opts port.EngineOptions
125 expected bool125 expected bool
126 }{126 }{
127 {127 {
128 name: "nil NetworkProcessMemory",128 name: "nil NetworkProcessMemory",
129- opts: port.WebKitContextOptions{},129+ opts: port.EngineOptions{},
130 expected: false,130 expected: false,
131 },131 },
132 {132 {
133 name: "empty NetworkProcessMemory",133 name: "empty NetworkProcessMemory",
134- opts: port.WebKitContextOptions{134+ opts: port.EngineOptions{
135 NetworkProcessMemory: &port.MemoryPressureConfig{},135 NetworkProcessMemory: &port.MemoryPressureConfig{},
136 },136 },
137 expected: false,137 expected: false,
138 },138 },
139 {139 {
140 name: "configured NetworkProcessMemory",140 name: "configured NetworkProcessMemory",
141- opts: port.WebKitContextOptions{141+ opts: port.EngineOptions{
142 NetworkProcessMemory: &port.MemoryPressureConfig{142 NetworkProcessMemory: &port.MemoryPressureConfig{
143 PollIntervalSec: 20.0,143 PollIntervalSec: 20.0,
144 },144 },
@@ -149,7 +149,7 @@ func TestWebKitContextOptions_IsNetworkProcessMemoryConfigured(t *testing.T) {
149 149
150 for _, tt := range tests {150 for _, tt := range tests {
151 t.Run(tt.name, func(t *testing.T) {151 t.Run(tt.name, func(t *testing.T) {
152- result := tt.opts.IsNetworkProcessMemoryConfigured()152+ result := tt.opts.NetworkProcessMemory.IsConfigured()
153 assert.Equal(t, tt.expected, result)153 assert.Equal(t, tt.expected, result)
154 })154 })
155 }155 }
M internal/application/port/mocks/mocks.go +445 -474
@@ -1188,6 +1188,63 @@ func (_c *MockContentInjector_InjectThemeCSS_Call) RunAndReturn(run func(ctx con
11881188 return _c
11891189 }
11901190
1191+// RefreshScripts provides a mock function for the type MockContentInjector
1192+func (_mock *MockContentInjector) RefreshScripts(ctx context.Context, wv port.WebView) error {
1193+ ret := _mock.Called(ctx, wv)
1194+
1195+ if len(ret) == 0 {
1196+ panic("no return value specified for RefreshScripts")
1197+ }
1198+
1199+ var r0 error
1200+ if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebView) error); ok {
1201+ r0 = returnFunc(ctx, wv)
1202+ } else {
1203+ r0 = ret.Error(0)
1204+ }
1205+ return r0
1206+}
1207+
1208+// MockContentInjector_RefreshScripts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RefreshScripts'
1209+type MockContentInjector_RefreshScripts_Call struct {
1210+ *mock.Call
1211+}
1212+
1213+// RefreshScripts is a helper method to define mock.On call
1214+// - ctx context.Context
1215+// - wv port.WebView
1216+func (_e *MockContentInjector_Expecter) RefreshScripts(ctx interface{}, wv interface{}) *MockContentInjector_RefreshScripts_Call {
1217+ return &MockContentInjector_RefreshScripts_Call{Call: _e.mock.On("RefreshScripts", ctx, wv)}
1218+}
1219+
1220+func (_c *MockContentInjector_RefreshScripts_Call) Run(run func(ctx context.Context, wv port.WebView)) *MockContentInjector_RefreshScripts_Call {
1221+ _c.Call.Run(func(args mock.Arguments) {
1222+ var arg0 context.Context
1223+ if args[0] != nil {
1224+ arg0 = args[0].(context.Context)
1225+ }
1226+ var arg1 port.WebView
1227+ if args[1] != nil {
1228+ arg1 = args[1].(port.WebView)
1229+ }
1230+ run(
1231+ arg0,
1232+ arg1,
1233+ )
1234+ })
1235+ return _c
1236+}
1237+
1238+func (_c *MockContentInjector_RefreshScripts_Call) Return(_a0 error) *MockContentInjector_RefreshScripts_Call {
1239+ _c.Call.Return(_a0)
1240+ return _c
1241+}
1242+
1243+func (_c *MockContentInjector_RefreshScripts_Call) RunAndReturn(run func(ctx context.Context, wv port.WebView) error) *MockContentInjector_RefreshScripts_Call {
1244+ _c.Call.Return(run)
1245+ return _c
1246+}
1247+
11911248 // NewMockDatabaseProvider creates a new instance of MockDatabaseProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
11921249 // The first argument is typically a *testing.T value.
11931250 func NewMockDatabaseProvider(t interface {
@@ -1524,6 +1581,158 @@ func (_c *MockDownloadResponse_GetUri_Call) RunAndReturn(run func() string) *Moc
15241581 return _c
15251582 }
15261583
1584+// NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1585+// The first argument is typically a *testing.T value.
1586+func NewMockMemoryPressureApplier(t interface {
1587+ mock.TestingT
1588+ Cleanup(func())
1589+}) *MockMemoryPressureApplier {
1590+ mock := &MockMemoryPressureApplier{}
1591+ mock.Mock.Test(t)
1592+
1593+ t.Cleanup(func() { mock.AssertExpectations(t) })
1594+
1595+ return mock
1596+}
1597+
1598+// MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type
1599+type MockMemoryPressureApplier struct {
1600+ mock.Mock
1601+}
1602+
1603+type MockMemoryPressureApplier_Expecter struct {
1604+ mock *mock.Mock
1605+}
1606+
1607+func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter {
1608+ return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock}
1609+}
1610+
1611+// ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier
1612+func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error {
1613+ ret := _mock.Called(ctx, cfg)
1614+
1615+ if len(ret) == 0 {
1616+ panic("no return value specified for ApplyNetworkProcessSettings")
1617+ }
1618+
1619+ var r0 error
1620+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok {
1621+ r0 = returnFunc(ctx, cfg)
1622+ } else {
1623+ r0 = ret.Error(0)
1624+ }
1625+ return r0
1626+}
1627+
1628+// MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings'
1629+type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct {
1630+ *mock.Call
1631+}
1632+
1633+// ApplyNetworkProcessSettings is a helper method to define mock.On call
1634+// - ctx context.Context
1635+// - cfg *port.MemoryPressureConfig
1636+func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1637+ return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)}
1638+}
1639+
1640+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1641+ _c.Call.Run(func(args mock.Arguments) {
1642+ var arg0 context.Context
1643+ if args[0] != nil {
1644+ arg0 = args[0].(context.Context)
1645+ }
1646+ var arg1 *port.MemoryPressureConfig
1647+ if args[1] != nil {
1648+ arg1 = args[1].(*port.MemoryPressureConfig)
1649+ }
1650+ run(
1651+ arg0,
1652+ arg1,
1653+ )
1654+ })
1655+ return _c
1656+}
1657+
1658+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1659+ _c.Call.Return(err)
1660+ return _c
1661+}
1662+
1663+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1664+ _c.Call.Return(run)
1665+ return _c
1666+}
1667+
1668+// ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier
1669+func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) {
1670+ ret := _mock.Called(ctx, cfg)
1671+
1672+ if len(ret) == 0 {
1673+ panic("no return value specified for ApplyWebProcessSettings")
1674+ }
1675+
1676+ var r0 any
1677+ var r1 error
1678+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok {
1679+ return returnFunc(ctx, cfg)
1680+ }
1681+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok {
1682+ r0 = returnFunc(ctx, cfg)
1683+ } else {
1684+ if ret.Get(0) != nil {
1685+ r0 = ret.Get(0).(any)
1686+ }
1687+ }
1688+ if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok {
1689+ r1 = returnFunc(ctx, cfg)
1690+ } else {
1691+ r1 = ret.Error(1)
1692+ }
1693+ return r0, r1
1694+}
1695+
1696+// MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings'
1697+type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct {
1698+ *mock.Call
1699+}
1700+
1701+// ApplyWebProcessSettings is a helper method to define mock.On call
1702+// - ctx context.Context
1703+// - cfg *port.MemoryPressureConfig
1704+func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1705+ return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)}
1706+}
1707+
1708+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1709+ _c.Call.Run(func(args mock.Arguments) {
1710+ var arg0 context.Context
1711+ if args[0] != nil {
1712+ arg0 = args[0].(context.Context)
1713+ }
1714+ var arg1 *port.MemoryPressureConfig
1715+ if args[1] != nil {
1716+ arg1 = args[1].(*port.MemoryPressureConfig)
1717+ }
1718+ run(
1719+ arg0,
1720+ arg1,
1721+ )
1722+ })
1723+ return _c
1724+}
1725+
1726+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1727+ _c.Call.Return(v, err)
1728+ return _c
1729+}
1730+
1731+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1732+ _c.Call.Return(run)
1733+ return _c
1734+}
1735+
15271736 // NewMockEngine creates a new instance of MockEngine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15281737 // The first argument is typically a *testing.T value.
15291738 func NewMockEngine(t interface {
@@ -1779,63 +1988,6 @@ func (_c *MockEngine_FilterApplier_Call) RunAndReturn(run func() port.FilterAppl
17791988 return _c
17801989 }
17811990
1782-// Init provides a mock function for the type MockEngine
1783-func (_mock *MockEngine) Init(ctx context.Context, opts port.EngineOptions) error {
1784- ret := _mock.Called(ctx, opts)
1785-
1786- if len(ret) == 0 {
1787- panic("no return value specified for Init")
1788- }
1789-
1790- var r0 error
1791- if returnFunc, ok := ret.Get(0).(func(context.Context, port.EngineOptions) error); ok {
1792- r0 = returnFunc(ctx, opts)
1793- } else {
1794- r0 = ret.Error(0)
1795- }
1796- return r0
1797-}
1798-
1799-// MockEngine_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init'
1800-type MockEngine_Init_Call struct {
1801- *mock.Call
1802-}
1803-
1804-// Init is a helper method to define mock.On call
1805-// - ctx context.Context
1806-// - opts port.EngineOptions
1807-func (_e *MockEngine_Expecter) Init(ctx interface{}, opts interface{}) *MockEngine_Init_Call {
1808- return &MockEngine_Init_Call{Call: _e.mock.On("Init", ctx, opts)}
1809-}
1810-
1811-func (_c *MockEngine_Init_Call) Run(run func(ctx context.Context, opts port.EngineOptions)) *MockEngine_Init_Call {
1812- _c.Call.Run(func(args mock.Arguments) {
1813- var arg0 context.Context
1814- if args[0] != nil {
1815- arg0 = args[0].(context.Context)
1816- }
1817- var arg1 port.EngineOptions
1818- if args[1] != nil {
1819- arg1 = args[1].(port.EngineOptions)
1820- }
1821- run(
1822- arg0,
1823- arg1,
1824- )
1825- })
1826- return _c
1827-}
1828-
1829-func (_c *MockEngine_Init_Call) Return(err error) *MockEngine_Init_Call {
1830- _c.Call.Return(err)
1831- return _c
1832-}
1833-
1834-func (_c *MockEngine_Init_Call) RunAndReturn(run func(ctx context.Context, opts port.EngineOptions) error) *MockEngine_Init_Call {
1835- _c.Call.Return(run)
1836- return _c
1837-}
1838-
18391991 // InternalSchemePath provides a mock function for the type MockEngine
18401992 func (_mock *MockEngine) InternalSchemePath() string {
18411993 ret := _mock.Called()
@@ -4339,13 +4491,13 @@ func (_c *MockFocusedInputProvider_SetFocusedInput_Call) RunAndReturn(run func(t
43394491 return _c
43404492 }
43414493
4342-// NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
4494+// NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
43434495 // The first argument is typically a *testing.T value.
4344-func NewMockMemoryPressureApplier(t interface {
4496+func NewMockWebView(t interface {
43454497 mock.TestingT
43464498 Cleanup(func())
4347-}) *MockMemoryPressureApplier {
4348- mock := &MockMemoryPressureApplier{}
4499+}) *MockWebView {
4500+ mock := &MockWebView{}
43494501 mock.Mock.Test(t)
43504502
43514503 t.Cleanup(func() { mock.AssertExpectations(t) })
@@ -4353,588 +4505,270 @@ func NewMockMemoryPressureApplier(t interface {
43534505 return mock
43544506 }
43554507
4356-// MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type
4357-type MockMemoryPressureApplier struct {
4508+// MockWebView is an autogenerated mock type for the WebView type
4509+type MockWebView struct {
43584510 mock.Mock
43594511 }
43604512
4361-type MockMemoryPressureApplier_Expecter struct {
4513+type MockWebView_Expecter struct {
43624514 mock *mock.Mock
43634515 }
43644516
4365-func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter {
4366- return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock}
4517+func (_m *MockWebView) EXPECT() *MockWebView_Expecter {
4518+ return &MockWebView_Expecter{mock: &_m.Mock}
43674519 }
43684520
4369-// ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier
4370-func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error {
4371- ret := _mock.Called(ctx, cfg)
4521+// CanGoBack provides a mock function for the type MockWebView
4522+func (_mock *MockWebView) CanGoBack() bool {
4523+ ret := _mock.Called()
43724524
43734525 if len(ret) == 0 {
4374- panic("no return value specified for ApplyNetworkProcessSettings")
4375- }
4376-
4377- var r0 error
4378- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok {
4379- r0 = returnFunc(ctx, cfg)
4380- } else {
4381- r0 = ret.Error(0)
4382- }
4383- return r0
4384-}
4385-
4386-// MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings'
4387-type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct {
4388- *mock.Call
4389-}
4390-
4391-// ApplyNetworkProcessSettings is a helper method to define mock.On call
4392-// - ctx context.Context
4393-// - cfg *port.MemoryPressureConfig
4394-func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4395- return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)}
4396-}
4397-
4398-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4399- _c.Call.Run(func(args mock.Arguments) {
4400- var arg0 context.Context
4401- if args[0] != nil {
4402- arg0 = args[0].(context.Context)
4403- }
4404- var arg1 *port.MemoryPressureConfig
4405- if args[1] != nil {
4406- arg1 = args[1].(*port.MemoryPressureConfig)
4407- }
4408- run(
4409- arg0,
4410- arg1,
4411- )
4412- })
4413- return _c
4414-}
4415-
4416-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4417- _c.Call.Return(err)
4418- return _c
4419-}
4420-
4421-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4422- _c.Call.Return(run)
4423- return _c
4424-}
4425-
4426-// ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier
4427-func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) {
4428- ret := _mock.Called(ctx, cfg)
4429-
4430- if len(ret) == 0 {
4431- panic("no return value specified for ApplyWebProcessSettings")
4432- }
4433-
4434- var r0 any
4435- var r1 error
4436- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok {
4437- return returnFunc(ctx, cfg)
4438- }
4439- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok {
4440- r0 = returnFunc(ctx, cfg)
4441- } else {
4442- if ret.Get(0) != nil {
4443- r0 = ret.Get(0).(any)
4444- }
4445- }
4446- if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok {
4447- r1 = returnFunc(ctx, cfg)
4448- } else {
4449- r1 = ret.Error(1)
4450- }
4451- return r0, r1
4452-}
4453-
4454-// MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings'
4455-type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct {
4456- *mock.Call
4457-}
4458-
4459-// ApplyWebProcessSettings is a helper method to define mock.On call
4460-// - ctx context.Context
4461-// - cfg *port.MemoryPressureConfig
4462-func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4463- return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)}
4464-}
4465-
4466-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4467- _c.Call.Run(func(args mock.Arguments) {
4468- var arg0 context.Context
4469- if args[0] != nil {
4470- arg0 = args[0].(context.Context)
4471- }
4472- var arg1 *port.MemoryPressureConfig
4473- if args[1] != nil {
4474- arg1 = args[1].(*port.MemoryPressureConfig)
4475- }
4476- run(
4477- arg0,
4478- arg1,
4479- )
4480- })
4481- return _c
4482-}
4483-
4484-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4485- _c.Call.Return(v, err)
4486- return _c
4487-}
4488-
4489-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4490- _c.Call.Return(run)
4491- return _c
4492-}
4493-
4494-// NewMockWebKitContextProvider creates a new instance of MockWebKitContextProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
4495-// The first argument is typically a *testing.T value.
4496-func NewMockWebKitContextProvider(t interface {
4497- mock.TestingT
4498- Cleanup(func())
4499-}) *MockWebKitContextProvider {
4500- mock := &MockWebKitContextProvider{}
4501- mock.Mock.Test(t)
4502-
4503- t.Cleanup(func() { mock.AssertExpectations(t) })
4504-
4505- return mock
4506-}
4507-
4508-// MockWebKitContextProvider is an autogenerated mock type for the WebKitContextProvider type
4509-type MockWebKitContextProvider struct {
4510- mock.Mock
4511-}
4512-
4513-type MockWebKitContextProvider_Expecter struct {
4514- mock *mock.Mock
4515-}
4516-
4517-func (_m *MockWebKitContextProvider) EXPECT() *MockWebKitContextProvider_Expecter {
4518- return &MockWebKitContextProvider_Expecter{mock: &_m.Mock}
4519-}
4520-
4521-// CacheDir provides a mock function for the type MockWebKitContextProvider
4522-func (_mock *MockWebKitContextProvider) CacheDir() string {
4523- ret := _mock.Called()
4524-
4525- if len(ret) == 0 {
4526- panic("no return value specified for CacheDir")
4527- }
4528-
4529- var r0 string
4530- if returnFunc, ok := ret.Get(0).(func() string); ok {
4531- r0 = returnFunc()
4532- } else {
4533- r0 = ret.Get(0).(string)
4534- }
4535- return r0
4536-}
4537-
4538-// MockWebKitContextProvider_CacheDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CacheDir'
4539-type MockWebKitContextProvider_CacheDir_Call struct {
4540- *mock.Call
4541-}
4542-
4543-// CacheDir is a helper method to define mock.On call
4544-func (_e *MockWebKitContextProvider_Expecter) CacheDir() *MockWebKitContextProvider_CacheDir_Call {
4545- return &MockWebKitContextProvider_CacheDir_Call{Call: _e.mock.On("CacheDir")}
4546-}
4547-
4548-func (_c *MockWebKitContextProvider_CacheDir_Call) Run(run func()) *MockWebKitContextProvider_CacheDir_Call {
4549- _c.Call.Run(func(args mock.Arguments) {
4550- run()
4551- })
4552- return _c
4553-}
4554-
4555-func (_c *MockWebKitContextProvider_CacheDir_Call) Return(s string) *MockWebKitContextProvider_CacheDir_Call {
4556- _c.Call.Return(s)
4557- return _c
4558-}
4559-
4560-func (_c *MockWebKitContextProvider_CacheDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_CacheDir_Call {
4561- _c.Call.Return(run)
4562- return _c
4563-}
4564-
4565-// Close provides a mock function for the type MockWebKitContextProvider
4566-func (_mock *MockWebKitContextProvider) Close() error {
4567- ret := _mock.Called()
4568-
4569- if len(ret) == 0 {
4570- panic("no return value specified for Close")
4526+ panic("no return value specified for CanGoBack")
45714527 }
45724528
4573- var r0 error
4574- if returnFunc, ok := ret.Get(0).(func() error); ok {
4529+ var r0 bool
4530+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
45754531 r0 = returnFunc()
45764532 } else {
4577- r0 = ret.Error(0)
4533+ r0 = ret.Get(0).(bool)
45784534 }
45794535 return r0
45804536 }
45814537
4582-// MockWebKitContextProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
4583-type MockWebKitContextProvider_Close_Call struct {
4538+// MockWebView_CanGoBack_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoBack'
4539+type MockWebView_CanGoBack_Call struct {
45844540 *mock.Call
45854541 }
45864542
4587-// Close is a helper method to define mock.On call
4588-func (_e *MockWebKitContextProvider_Expecter) Close() *MockWebKitContextProvider_Close_Call {
4589- return &MockWebKitContextProvider_Close_Call{Call: _e.mock.On("Close")}
4543+// CanGoBack is a helper method to define mock.On call
4544+func (_e *MockWebView_Expecter) CanGoBack() *MockWebView_CanGoBack_Call {
4545+ return &MockWebView_CanGoBack_Call{Call: _e.mock.On("CanGoBack")}
45904546 }
45914547
4592-func (_c *MockWebKitContextProvider_Close_Call) Run(run func()) *MockWebKitContextProvider_Close_Call {
4548+func (_c *MockWebView_CanGoBack_Call) Run(run func()) *MockWebView_CanGoBack_Call {
45934549 _c.Call.Run(func(args mock.Arguments) {
45944550 run()
45954551 })
45964552 return _c
45974553 }
45984554
4599-func (_c *MockWebKitContextProvider_Close_Call) Return(err error) *MockWebKitContextProvider_Close_Call {
4600- _c.Call.Return(err)
4555+func (_c *MockWebView_CanGoBack_Call) Return(b bool) *MockWebView_CanGoBack_Call {
4556+ _c.Call.Return(b)
46014557 return _c
46024558 }
46034559
4604-func (_c *MockWebKitContextProvider_Close_Call) RunAndReturn(run func() error) *MockWebKitContextProvider_Close_Call {
4560+func (_c *MockWebView_CanGoBack_Call) RunAndReturn(run func() bool) *MockWebView_CanGoBack_Call {
46054561 _c.Call.Return(run)
46064562 return _c
46074563 }
46084564
4609-// DataDir provides a mock function for the type MockWebKitContextProvider
4610-func (_mock *MockWebKitContextProvider) DataDir() string {
4565+// CanGoForward provides a mock function for the type MockWebView
4566+func (_mock *MockWebView) CanGoForward() bool {
46114567 ret := _mock.Called()
46124568
46134569 if len(ret) == 0 {
4614- panic("no return value specified for DataDir")
4570+ panic("no return value specified for CanGoForward")
46154571 }
46164572
4617- var r0 string
4618- if returnFunc, ok := ret.Get(0).(func() string); ok {
4573+ var r0 bool
4574+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
46194575 r0 = returnFunc()
46204576 } else {
4621- r0 = ret.Get(0).(string)
4577+ r0 = ret.Get(0).(bool)
46224578 }
46234579 return r0
46244580 }
46254581
4626-// MockWebKitContextProvider_DataDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DataDir'
4627-type MockWebKitContextProvider_DataDir_Call struct {
4582+// MockWebView_CanGoForward_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoForward'
4583+type MockWebView_CanGoForward_Call struct {
46284584 *mock.Call
46294585 }
46304586
4631-// DataDir is a helper method to define mock.On call
4632-func (_e *MockWebKitContextProvider_Expecter) DataDir() *MockWebKitContextProvider_DataDir_Call {
4633- return &MockWebKitContextProvider_DataDir_Call{Call: _e.mock.On("DataDir")}
4587+// CanGoForward is a helper method to define mock.On call
4588+func (_e *MockWebView_Expecter) CanGoForward() *MockWebView_CanGoForward_Call {
4589+ return &MockWebView_CanGoForward_Call{Call: _e.mock.On("CanGoForward")}
46344590 }
46354591
4636-func (_c *MockWebKitContextProvider_DataDir_Call) Run(run func()) *MockWebKitContextProvider_DataDir_Call {
4592+func (_c *MockWebView_CanGoForward_Call) Run(run func()) *MockWebView_CanGoForward_Call {
46374593 _c.Call.Run(func(args mock.Arguments) {
46384594 run()
46394595 })
46404596 return _c
46414597 }
46424598
4643-func (_c *MockWebKitContextProvider_DataDir_Call) Return(s string) *MockWebKitContextProvider_DataDir_Call {
4644- _c.Call.Return(s)
4645- return _c
4646-}
4647-
4648-func (_c *MockWebKitContextProvider_DataDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_DataDir_Call {
4649- _c.Call.Return(run)
4650- return _c
4651-}
4652-
4653-// Initialize provides a mock function for the type MockWebKitContextProvider
4654-func (_mock *MockWebKitContextProvider) Initialize(ctx context.Context, opts port.WebKitContextOptions) error {
4655- ret := _mock.Called(ctx, opts)
4656-
4657- if len(ret) == 0 {
4658- panic("no return value specified for Initialize")
4659- }
4660-
4661- var r0 error
4662- if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebKitContextOptions) error); ok {
4663- r0 = returnFunc(ctx, opts)
4664- } else {
4665- r0 = ret.Error(0)
4666- }
4667- return r0
4668-}
4669-
4670-// MockWebKitContextProvider_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize'
4671-type MockWebKitContextProvider_Initialize_Call struct {
4672- *mock.Call
4673-}
4674-
4675-// Initialize is a helper method to define mock.On call
4676-// - ctx context.Context
4677-// - opts port.WebKitContextOptions
4678-func (_e *MockWebKitContextProvider_Expecter) Initialize(ctx interface{}, opts interface{}) *MockWebKitContextProvider_Initialize_Call {
4679- return &MockWebKitContextProvider_Initialize_Call{Call: _e.mock.On("Initialize", ctx, opts)}
4680-}
4681-
4682-func (_c *MockWebKitContextProvider_Initialize_Call) Run(run func(ctx context.Context, opts port.WebKitContextOptions)) *MockWebKitContextProvider_Initialize_Call {
4683- _c.Call.Run(func(args mock.Arguments) {
4684- var arg0 context.Context
4685- if args[0] != nil {
4686- arg0 = args[0].(context.Context)
4687- }
4688- var arg1 port.WebKitContextOptions
4689- if args[1] != nil {
4690- arg1 = args[1].(port.WebKitContextOptions)
4691- }
4692- run(
4693- arg0,
4694- arg1,
4695- )
4696- })
4697- return _c
4698-}
4699-
4700-func (_c *MockWebKitContextProvider_Initialize_Call) Return(err error) *MockWebKitContextProvider_Initialize_Call {
4701- _c.Call.Return(err)
4599+func (_c *MockWebView_CanGoForward_Call) Return(b bool) *MockWebView_CanGoForward_Call {
4600+ _c.Call.Return(b)
47024601 return _c
47034602 }
47044603
4705-func (_c *MockWebKitContextProvider_Initialize_Call) RunAndReturn(run func(ctx context.Context, opts port.WebKitContextOptions) error) *MockWebKitContextProvider_Initialize_Call {
4604+func (_c *MockWebView_CanGoForward_Call) RunAndReturn(run func() bool) *MockWebView_CanGoForward_Call {
47064605 _c.Call.Return(run)
47074606 return _c
47084607 }
47094608
4710-// PrefetchDNS provides a mock function for the type MockWebKitContextProvider
4711-func (_mock *MockWebKitContextProvider) PrefetchDNS(hostname string) {
4712- _mock.Called(hostname)
4609+// Destroy provides a mock function for the type MockWebView
4610+func (_mock *MockWebView) Destroy() {
4611+ _mock.Called()
47134612 return
47144613 }
47154614
4716-// MockWebKitContextProvider_PrefetchDNS_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrefetchDNS'
4717-type MockWebKitContextProvider_PrefetchDNS_Call struct {
4615+// MockWebView_Destroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destroy'
4616+type MockWebView_Destroy_Call struct {
47184617 *mock.Call
47194618 }
47204619
4721-// PrefetchDNS is a helper method to define mock.On call
4722-// - hostname string
4723-func (_e *MockWebKitContextProvider_Expecter) PrefetchDNS(hostname interface{}) *MockWebKitContextProvider_PrefetchDNS_Call {
4724- return &MockWebKitContextProvider_PrefetchDNS_Call{Call: _e.mock.On("PrefetchDNS", hostname)}
4620+// Destroy is a helper method to define mock.On call
4621+func (_e *MockWebView_Expecter) Destroy() *MockWebView_Destroy_Call {
4622+ return &MockWebView_Destroy_Call{Call: _e.mock.On("Destroy")}
47254623 }
47264624
4727-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Run(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call {
4625+func (_c *MockWebView_Destroy_Call) Run(run func()) *MockWebView_Destroy_Call {
47284626 _c.Call.Run(func(args mock.Arguments) {
4729- var arg0 string
4730- if args[0] != nil {
4731- arg0 = args[0].(string)
4732- }
4733- run(
4734- arg0,
4735- )
4627+ run()
47364628 })
47374629 return _c
47384630 }
47394631
4740-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Return() *MockWebKitContextProvider_PrefetchDNS_Call {
4632+func (_c *MockWebView_Destroy_Call) Return() *MockWebView_Destroy_Call {
47414633 _c.Call.Return()
47424634 return _c
47434635 }
47444636
4745-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) RunAndReturn(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call {
4637+func (_c *MockWebView_Destroy_Call) RunAndReturn(run func()) *MockWebView_Destroy_Call {
47464638 _c.Run(run)
47474639 return _c
47484640 }
47494641
4750-// NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
4751-// The first argument is typically a *testing.T value.
4752-func NewMockWebView(t interface {
4753- mock.TestingT
4754- Cleanup(func())
4755-}) *MockWebView {
4756- mock := &MockWebView{}
4757- mock.Mock.Test(t)
4758-
4759- t.Cleanup(func() { mock.AssertExpectations(t) })
4760-
4761- return mock
4762-}
4763-
4764-// MockWebView is an autogenerated mock type for the WebView type
4765-type MockWebView struct {
4766- mock.Mock
4767-}
4768-
4769-type MockWebView_Expecter struct {
4770- mock *mock.Mock
4771-}
4772-
4773-func (_m *MockWebView) EXPECT() *MockWebView_Expecter {
4774- return &MockWebView_Expecter{mock: &_m.Mock}
4775-}
4776-
4777-// CanGoBack provides a mock function for the type MockWebView
4778-func (_mock *MockWebView) CanGoBack() bool {
4642+// EstimatedProgress provides a mock function for the type MockWebView
4643+func (_mock *MockWebView) EstimatedProgress() float64 {
47794644 ret := _mock.Called()
47804645
47814646 if len(ret) == 0 {
4782- panic("no return value specified for CanGoBack")
4647+ panic("no return value specified for EstimatedProgress")
47834648 }
47844649
4785- var r0 bool
4786- if returnFunc, ok := ret.Get(0).(func() bool); ok {
4650+ var r0 float64
4651+ if returnFunc, ok := ret.Get(0).(func() float64); ok {
47874652 r0 = returnFunc()
47884653 } else {
4789- r0 = ret.Get(0).(bool)
4654+ r0 = ret.Get(0).(float64)
47904655 }
47914656 return r0
47924657 }
47934658
4794-// MockWebView_CanGoBack_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoBack'
4795-type MockWebView_CanGoBack_Call struct {
4659+// MockWebView_EstimatedProgress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimatedProgress'
4660+type MockWebView_EstimatedProgress_Call struct {
47964661 *mock.Call
47974662 }
47984663
4799-// CanGoBack is a helper method to define mock.On call
4800-func (_e *MockWebView_Expecter) CanGoBack() *MockWebView_CanGoBack_Call {
4801- return &MockWebView_CanGoBack_Call{Call: _e.mock.On("CanGoBack")}
4664+// EstimatedProgress is a helper method to define mock.On call
4665+func (_e *MockWebView_Expecter) EstimatedProgress() *MockWebView_EstimatedProgress_Call {
4666+ return &MockWebView_EstimatedProgress_Call{Call: _e.mock.On("EstimatedProgress")}
48024667 }
48034668
4804-func (_c *MockWebView_CanGoBack_Call) Run(run func()) *MockWebView_CanGoBack_Call {
4669+func (_c *MockWebView_EstimatedProgress_Call) Run(run func()) *MockWebView_EstimatedProgress_Call {
48054670 _c.Call.Run(func(args mock.Arguments) {
48064671 run()
48074672 })
48084673 return _c
48094674 }
48104675
4811-func (_c *MockWebView_CanGoBack_Call) Return(b bool) *MockWebView_CanGoBack_Call {
4812- _c.Call.Return(b)
4676+func (_c *MockWebView_EstimatedProgress_Call) Return(f float64) *MockWebView_EstimatedProgress_Call {
4677+ _c.Call.Return(f)
48134678 return _c
48144679 }
48154680
4816-func (_c *MockWebView_CanGoBack_Call) RunAndReturn(run func() bool) *MockWebView_CanGoBack_Call {
4681+func (_c *MockWebView_EstimatedProgress_Call) RunAndReturn(run func() float64) *MockWebView_EstimatedProgress_Call {
48174682 _c.Call.Return(run)
48184683 return _c
48194684 }
48204685
4821-// CanGoForward provides a mock function for the type MockWebView
4822-func (_mock *MockWebView) CanGoForward() bool {
4686+// Favicon provides a mock function for the type MockWebView
4687+func (_mock *MockWebView) Favicon() port.Texture {
48234688 ret := _mock.Called()
48244689
48254690 if len(ret) == 0 {
4826- panic("no return value specified for CanGoForward")
4691+ panic("no return value specified for Favicon")
48274692 }
48284693
4829- var r0 bool
4830- if returnFunc, ok := ret.Get(0).(func() bool); ok {
4694+ var r0 port.Texture
4695+ if returnFunc, ok := ret.Get(0).(func() port.Texture); ok {
48314696 r0 = returnFunc()
48324697 } else {
4833- r0 = ret.Get(0).(bool)
4698+ if ret.Get(0) != nil {
4699+ r0 = ret.Get(0).(port.Texture)
4700+ }
48344701 }
48354702 return r0
48364703 }
48374704
4838-// MockWebView_CanGoForward_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoForward'
4839-type MockWebView_CanGoForward_Call struct {
4705+// MockWebView_Favicon_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Favicon'
4706+type MockWebView_Favicon_Call struct {
48404707 *mock.Call
48414708 }
48424709
4843-// CanGoForward is a helper method to define mock.On call
4844-func (_e *MockWebView_Expecter) CanGoForward() *MockWebView_CanGoForward_Call {
4845- return &MockWebView_CanGoForward_Call{Call: _e.mock.On("CanGoForward")}
4710+// Favicon is a helper method to define mock.On call
4711+func (_e *MockWebView_Expecter) Favicon() *MockWebView_Favicon_Call {
4712+ return &MockWebView_Favicon_Call{Call: _e.mock.On("Favicon")}
48464713 }
48474714
4848-func (_c *MockWebView_CanGoForward_Call) Run(run func()) *MockWebView_CanGoForward_Call {
4715+func (_c *MockWebView_Favicon_Call) Run(run func()) *MockWebView_Favicon_Call {
48494716 _c.Call.Run(func(args mock.Arguments) {
48504717 run()
48514718 })
48524719 return _c
48534720 }
48544721
4855-func (_c *MockWebView_CanGoForward_Call) Return(b bool) *MockWebView_CanGoForward_Call {
4856- _c.Call.Return(b)
4722+func (_c *MockWebView_Favicon_Call) Return(texture port.Texture) *MockWebView_Favicon_Call {
4723+ _c.Call.Return(texture)
48574724 return _c
48584725 }
48594726
4860-func (_c *MockWebView_CanGoForward_Call) RunAndReturn(run func() bool) *MockWebView_CanGoForward_Call {
4727+func (_c *MockWebView_Favicon_Call) RunAndReturn(run func() port.Texture) *MockWebView_Favicon_Call {
48614728 _c.Call.Return(run)
48624729 return _c
48634730 }
48644731
4865-// Destroy provides a mock function for the type MockWebView
4866-func (_mock *MockWebView) Destroy() {
4867- _mock.Called()
4868- return
4869-}
4870-
4871-// MockWebView_Destroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destroy'
4872-type MockWebView_Destroy_Call struct {
4873- *mock.Call
4874-}
4875-
4876-// Destroy is a helper method to define mock.On call
4877-func (_e *MockWebView_Expecter) Destroy() *MockWebView_Destroy_Call {
4878- return &MockWebView_Destroy_Call{Call: _e.mock.On("Destroy")}
4879-}
4880-
4881-func (_c *MockWebView_Destroy_Call) Run(run func()) *MockWebView_Destroy_Call {
4882- _c.Call.Run(func(args mock.Arguments) {
4883- run()
4884- })
4885- return _c
4886-}
4887-
4888-func (_c *MockWebView_Destroy_Call) Return() *MockWebView_Destroy_Call {
4889- _c.Call.Return()
4890- return _c
4891-}
4892-
4893-func (_c *MockWebView_Destroy_Call) RunAndReturn(run func()) *MockWebView_Destroy_Call {
4894- _c.Run(run)
4895- return _c
4896-}
4897-
4898-// EstimatedProgress provides a mock function for the type MockWebView
4899-func (_mock *MockWebView) EstimatedProgress() float64 {
4732+// Generation provides a mock function for the type MockWebView
4733+func (_mock *MockWebView) Generation() uint64 {
49004734 ret := _mock.Called()
49014735
49024736 if len(ret) == 0 {
4903- panic("no return value specified for EstimatedProgress")
4737+ panic("no return value specified for Generation")
49044738 }
49054739
4906- var r0 float64
4907- if returnFunc, ok := ret.Get(0).(func() float64); ok {
4740+ var r0 uint64
4741+ if returnFunc, ok := ret.Get(0).(func() uint64); ok {
49084742 r0 = returnFunc()
49094743 } else {
4910- r0 = ret.Get(0).(float64)
4744+ r0 = ret.Get(0).(uint64)
49114745 }
49124746 return r0
49134747 }
49144748
4915-// MockWebView_EstimatedProgress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimatedProgress'
4916-type MockWebView_EstimatedProgress_Call struct {
4749+// MockWebView_Generation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Generation'
4750+type MockWebView_Generation_Call struct {
49174751 *mock.Call
49184752 }
49194753
4920-// EstimatedProgress is a helper method to define mock.On call
4921-func (_e *MockWebView_Expecter) EstimatedProgress() *MockWebView_EstimatedProgress_Call {
4922- return &MockWebView_EstimatedProgress_Call{Call: _e.mock.On("EstimatedProgress")}
4754+// Generation is a helper method to define mock.On call
4755+func (_e *MockWebView_Expecter) Generation() *MockWebView_Generation_Call {
4756+ return &MockWebView_Generation_Call{Call: _e.mock.On("Generation")}
49234757 }
49244758
4925-func (_c *MockWebView_EstimatedProgress_Call) Run(run func()) *MockWebView_EstimatedProgress_Call {
4759+func (_c *MockWebView_Generation_Call) Run(run func()) *MockWebView_Generation_Call {
49264760 _c.Call.Run(func(args mock.Arguments) {
49274761 run()
49284762 })
49294763 return _c
49304764 }
49314765
4932-func (_c *MockWebView_EstimatedProgress_Call) Return(f float64) *MockWebView_EstimatedProgress_Call {
4933- _c.Call.Return(f)
4766+func (_c *MockWebView_Generation_Call) Return(v uint64) *MockWebView_Generation_Call {
4767+ _c.Call.Return(v)
49344768 return _c
49354769 }
49364770
4937-func (_c *MockWebView_EstimatedProgress_Call) RunAndReturn(run func() float64) *MockWebView_EstimatedProgress_Call {
4771+func (_c *MockWebView_Generation_Call) RunAndReturn(run func() uint64) *MockWebView_Generation_Call {
49384772 _c.Call.Return(run)
49394773 return _c
49404774 }
@@ -5573,6 +5407,143 @@ func (_c *MockWebView_ReloadBypassCache_Call) RunAndReturn(run func(ctx context.
55735407 return _c
55745408 }
55755409
5410+// ResetBackgroundToDefault provides a mock function for the type MockWebView
5411+func (_mock *MockWebView) ResetBackgroundToDefault() {
5412+ _mock.Called()
5413+ return
5414+}
5415+
5416+// MockWebView_ResetBackgroundToDefault_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResetBackgroundToDefault'
5417+type MockWebView_ResetBackgroundToDefault_Call struct {
5418+ *mock.Call
5419+}
5420+
5421+// ResetBackgroundToDefault is a helper method to define mock.On call
5422+func (_e *MockWebView_Expecter) ResetBackgroundToDefault() *MockWebView_ResetBackgroundToDefault_Call {
5423+ return &MockWebView_ResetBackgroundToDefault_Call{Call: _e.mock.On("ResetBackgroundToDefault")}
5424+}
5425+
5426+func (_c *MockWebView_ResetBackgroundToDefault_Call) Run(run func()) *MockWebView_ResetBackgroundToDefault_Call {
5427+ _c.Call.Run(func(args mock.Arguments) {
5428+ run()
5429+ })
5430+ return _c
5431+}
5432+
5433+func (_c *MockWebView_ResetBackgroundToDefault_Call) Return() *MockWebView_ResetBackgroundToDefault_Call {
5434+ _c.Call.Return()
5435+ return _c
5436+}
5437+
5438+func (_c *MockWebView_ResetBackgroundToDefault_Call) RunAndReturn(run func()) *MockWebView_ResetBackgroundToDefault_Call {
5439+ _c.Run(run)
5440+ return _c
5441+}
5442+
5443+// RunJavaScript provides a mock function for the type MockWebView
5444+func (_mock *MockWebView) RunJavaScript(ctx context.Context, script string) {
5445+ _mock.Called(ctx, script)
5446+ return
5447+}
5448+
5449+// MockWebView_RunJavaScript_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RunJavaScript'
5450+type MockWebView_RunJavaScript_Call struct {
5451+ *mock.Call
5452+}
5453+
5454+// RunJavaScript is a helper method to define mock.On call
5455+// - ctx context.Context
5456+// - script string
5457+func (_e *MockWebView_Expecter) RunJavaScript(ctx interface{}, script interface{}) *MockWebView_RunJavaScript_Call {
5458+ return &MockWebView_RunJavaScript_Call{Call: _e.mock.On("RunJavaScript", ctx, script)}
5459+}
5460+
5461+func (_c *MockWebView_RunJavaScript_Call) Run(run func(ctx context.Context, script string)) *MockWebView_RunJavaScript_Call {
5462+ _c.Call.Run(func(args mock.Arguments) {
5463+ var arg0 context.Context
5464+ if args[0] != nil {
5465+ arg0 = args[0].(context.Context)
5466+ }
5467+ var arg1 string
5468+ if args[1] != nil {
5469+ arg1 = args[1].(string)
5470+ }
5471+ run(
5472+ arg0,
5473+ arg1,
5474+ )
5475+ })
5476+ return _c
5477+}
5478+
5479+func (_c *MockWebView_RunJavaScript_Call) Return() *MockWebView_RunJavaScript_Call {
5480+ _c.Call.Return()
5481+ return _c
5482+}
5483+
5484+func (_c *MockWebView_RunJavaScript_Call) RunAndReturn(run func(ctx context.Context, script string)) *MockWebView_RunJavaScript_Call {
5485+ _c.Run(run)
5486+ return _c
5487+}
5488+
5489+// SetBackgroundColor provides a mock function for the type MockWebView
5490+func (_mock *MockWebView) SetBackgroundColor(r float64, g float64, b float64, a float64) {
5491+ _mock.Called(r, g, b, a)
5492+ return
5493+}
5494+
5495+// MockWebView_SetBackgroundColor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetBackgroundColor'
5496+type MockWebView_SetBackgroundColor_Call struct {
5497+ *mock.Call
5498+}
5499+
5500+// SetBackgroundColor is a helper method to define mock.On call
5501+// - r float64
5502+// - g float64
5503+// - b float64
5504+// - a float64
5505+func (_e *MockWebView_Expecter) SetBackgroundColor(r interface{}, g interface{}, b interface{}, a interface{}) *MockWebView_SetBackgroundColor_Call {
5506+ return &MockWebView_SetBackgroundColor_Call{Call: _e.mock.On("SetBackgroundColor", r, g, b, a)}
5507+}
5508+
5509+func (_c *MockWebView_SetBackgroundColor_Call) Run(run func(r float64, g float64, b float64, a float64)) *MockWebView_SetBackgroundColor_Call {
5510+ _c.Call.Run(func(args mock.Arguments) {
5511+ var arg0 float64
5512+ if args[0] != nil {
5513+ arg0 = args[0].(float64)
5514+ }
5515+ var arg1 float64
5516+ if args[1] != nil {
5517+ arg1 = args[1].(float64)
5518+ }
5519+ var arg2 float64
5520+ if args[2] != nil {
5521+ arg2 = args[2].(float64)
5522+ }
5523+ var arg3 float64
5524+ if args[3] != nil {
5525+ arg3 = args[3].(float64)
5526+ }
5527+ run(
5528+ arg0,
5529+ arg1,
5530+ arg2,
5531+ arg3,
5532+ )
5533+ })
5534+ return _c
5535+}
5536+
5537+func (_c *MockWebView_SetBackgroundColor_Call) Return() *MockWebView_SetBackgroundColor_Call {
5538+ _c.Call.Return()
5539+ return _c
5540+}
5541+
5542+func (_c *MockWebView_SetBackgroundColor_Call) RunAndReturn(run func(r float64, g float64, b float64, a float64)) *MockWebView_SetBackgroundColor_Call {
5543+ _c.Run(run)
5544+ return _c
5545+}
5546+
55765547 // SetCallbacks provides a mock function for the type MockWebView
55775548 func (_mock *MockWebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
55785549 _mock.Called(callbacks)
@@ -1188,6 +1188,63 @@ func (_c *MockContentInjector_InjectThemeCSS_Call) RunAndReturn(run func(ctx con
1188 return _c1188 return _c
1189 }1189 }
1190 1190
1191+// RefreshScripts provides a mock function for the type MockContentInjector
1192+func (_mock *MockContentInjector) RefreshScripts(ctx context.Context, wv port.WebView) error {
1193+ ret := _mock.Called(ctx, wv)
1194+
1195+ if len(ret) == 0 {
1196+ panic("no return value specified for RefreshScripts")
1197+ }
1198+
1199+ var r0 error
1200+ if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebView) error); ok {
1201+ r0 = returnFunc(ctx, wv)
1202+ } else {
1203+ r0 = ret.Error(0)
1204+ }
1205+ return r0
1206+}
1207+
1208+// MockContentInjector_RefreshScripts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RefreshScripts'
1209+type MockContentInjector_RefreshScripts_Call struct {
1210+ *mock.Call
1211+}
1212+
1213+// RefreshScripts is a helper method to define mock.On call
1214+// - ctx context.Context
1215+// - wv port.WebView
1216+func (_e *MockContentInjector_Expecter) RefreshScripts(ctx interface{}, wv interface{}) *MockContentInjector_RefreshScripts_Call {
1217+ return &MockContentInjector_RefreshScripts_Call{Call: _e.mock.On("RefreshScripts", ctx, wv)}
1218+}
1219+
1220+func (_c *MockContentInjector_RefreshScripts_Call) Run(run func(ctx context.Context, wv port.WebView)) *MockContentInjector_RefreshScripts_Call {
1221+ _c.Call.Run(func(args mock.Arguments) {
1222+ var arg0 context.Context
1223+ if args[0] != nil {
1224+ arg0 = args[0].(context.Context)
1225+ }
1226+ var arg1 port.WebView
1227+ if args[1] != nil {
1228+ arg1 = args[1].(port.WebView)
1229+ }
1230+ run(
1231+ arg0,
1232+ arg1,
1233+ )
1234+ })
1235+ return _c
1236+}
1237+
1238+func (_c *MockContentInjector_RefreshScripts_Call) Return(_a0 error) *MockContentInjector_RefreshScripts_Call {
1239+ _c.Call.Return(_a0)
1240+ return _c
1241+}
1242+
1243+func (_c *MockContentInjector_RefreshScripts_Call) RunAndReturn(run func(ctx context.Context, wv port.WebView) error) *MockContentInjector_RefreshScripts_Call {
1244+ _c.Call.Return(run)
1245+ return _c
1246+}
1247+
1191 // NewMockDatabaseProvider creates a new instance of MockDatabaseProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.1248 // NewMockDatabaseProvider creates a new instance of MockDatabaseProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1192 // The first argument is typically a *testing.T value.1249 // The first argument is typically a *testing.T value.
1193 func NewMockDatabaseProvider(t interface {1250 func NewMockDatabaseProvider(t interface {
@@ -1524,6 +1581,158 @@ func (_c *MockDownloadResponse_GetUri_Call) RunAndReturn(run func() string) *Moc
1524 return _c1581 return _c
1525 }1582 }
1526 1583
1584+// NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1585+// The first argument is typically a *testing.T value.
1586+func NewMockMemoryPressureApplier(t interface {
1587+ mock.TestingT
1588+ Cleanup(func())
1589+}) *MockMemoryPressureApplier {
1590+ mock := &MockMemoryPressureApplier{}
1591+ mock.Mock.Test(t)
1592+
1593+ t.Cleanup(func() { mock.AssertExpectations(t) })
1594+
1595+ return mock
1596+}
1597+
1598+// MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type
1599+type MockMemoryPressureApplier struct {
1600+ mock.Mock
1601+}
1602+
1603+type MockMemoryPressureApplier_Expecter struct {
1604+ mock *mock.Mock
1605+}
1606+
1607+func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter {
1608+ return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock}
1609+}
1610+
1611+// ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier
1612+func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error {
1613+ ret := _mock.Called(ctx, cfg)
1614+
1615+ if len(ret) == 0 {
1616+ panic("no return value specified for ApplyNetworkProcessSettings")
1617+ }
1618+
1619+ var r0 error
1620+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok {
1621+ r0 = returnFunc(ctx, cfg)
1622+ } else {
1623+ r0 = ret.Error(0)
1624+ }
1625+ return r0
1626+}
1627+
1628+// MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings'
1629+type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct {
1630+ *mock.Call
1631+}
1632+
1633+// ApplyNetworkProcessSettings is a helper method to define mock.On call
1634+// - ctx context.Context
1635+// - cfg *port.MemoryPressureConfig
1636+func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1637+ return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)}
1638+}
1639+
1640+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1641+ _c.Call.Run(func(args mock.Arguments) {
1642+ var arg0 context.Context
1643+ if args[0] != nil {
1644+ arg0 = args[0].(context.Context)
1645+ }
1646+ var arg1 *port.MemoryPressureConfig
1647+ if args[1] != nil {
1648+ arg1 = args[1].(*port.MemoryPressureConfig)
1649+ }
1650+ run(
1651+ arg0,
1652+ arg1,
1653+ )
1654+ })
1655+ return _c
1656+}
1657+
1658+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1659+ _c.Call.Return(err)
1660+ return _c
1661+}
1662+
1663+func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
1664+ _c.Call.Return(run)
1665+ return _c
1666+}
1667+
1668+// ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier
1669+func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) {
1670+ ret := _mock.Called(ctx, cfg)
1671+
1672+ if len(ret) == 0 {
1673+ panic("no return value specified for ApplyWebProcessSettings")
1674+ }
1675+
1676+ var r0 any
1677+ var r1 error
1678+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok {
1679+ return returnFunc(ctx, cfg)
1680+ }
1681+ if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok {
1682+ r0 = returnFunc(ctx, cfg)
1683+ } else {
1684+ if ret.Get(0) != nil {
1685+ r0 = ret.Get(0).(any)
1686+ }
1687+ }
1688+ if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok {
1689+ r1 = returnFunc(ctx, cfg)
1690+ } else {
1691+ r1 = ret.Error(1)
1692+ }
1693+ return r0, r1
1694+}
1695+
1696+// MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings'
1697+type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct {
1698+ *mock.Call
1699+}
1700+
1701+// ApplyWebProcessSettings is a helper method to define mock.On call
1702+// - ctx context.Context
1703+// - cfg *port.MemoryPressureConfig
1704+func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1705+ return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)}
1706+}
1707+
1708+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1709+ _c.Call.Run(func(args mock.Arguments) {
1710+ var arg0 context.Context
1711+ if args[0] != nil {
1712+ arg0 = args[0].(context.Context)
1713+ }
1714+ var arg1 *port.MemoryPressureConfig
1715+ if args[1] != nil {
1716+ arg1 = args[1].(*port.MemoryPressureConfig)
1717+ }
1718+ run(
1719+ arg0,
1720+ arg1,
1721+ )
1722+ })
1723+ return _c
1724+}
1725+
1726+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1727+ _c.Call.Return(v, err)
1728+ return _c
1729+}
1730+
1731+func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
1732+ _c.Call.Return(run)
1733+ return _c
1734+}
1735+
1527 // NewMockEngine creates a new instance of MockEngine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.1736 // NewMockEngine creates a new instance of MockEngine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1528 // The first argument is typically a *testing.T value.1737 // The first argument is typically a *testing.T value.
1529 func NewMockEngine(t interface {1738 func NewMockEngine(t interface {
@@ -1779,63 +1988,6 @@ func (_c *MockEngine_FilterApplier_Call) RunAndReturn(run func() port.FilterAppl
1779 return _c1988 return _c
1780 }1989 }
1781 1990
1782-// Init provides a mock function for the type MockEngine
1783-func (_mock *MockEngine) Init(ctx context.Context, opts port.EngineOptions) error {
1784- ret := _mock.Called(ctx, opts)
1785-
1786- if len(ret) == 0 {
1787- panic("no return value specified for Init")
1788- }
1789-
1790- var r0 error
1791- if returnFunc, ok := ret.Get(0).(func(context.Context, port.EngineOptions) error); ok {
1792- r0 = returnFunc(ctx, opts)
1793- } else {
1794- r0 = ret.Error(0)
1795- }
1796- return r0
1797-}
1798-
1799-// MockEngine_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init'
1800-type MockEngine_Init_Call struct {
1801- *mock.Call
1802-}
1803-
1804-// Init is a helper method to define mock.On call
1805-// - ctx context.Context
1806-// - opts port.EngineOptions
1807-func (_e *MockEngine_Expecter) Init(ctx interface{}, opts interface{}) *MockEngine_Init_Call {
1808- return &MockEngine_Init_Call{Call: _e.mock.On("Init", ctx, opts)}
1809-}
1810-
1811-func (_c *MockEngine_Init_Call) Run(run func(ctx context.Context, opts port.EngineOptions)) *MockEngine_Init_Call {
1812- _c.Call.Run(func(args mock.Arguments) {
1813- var arg0 context.Context
1814- if args[0] != nil {
1815- arg0 = args[0].(context.Context)
1816- }
1817- var arg1 port.EngineOptions
1818- if args[1] != nil {
1819- arg1 = args[1].(port.EngineOptions)
1820- }
1821- run(
1822- arg0,
1823- arg1,
1824- )
1825- })
1826- return _c
1827-}
1828-
1829-func (_c *MockEngine_Init_Call) Return(err error) *MockEngine_Init_Call {
1830- _c.Call.Return(err)
1831- return _c
1832-}
1833-
1834-func (_c *MockEngine_Init_Call) RunAndReturn(run func(ctx context.Context, opts port.EngineOptions) error) *MockEngine_Init_Call {
1835- _c.Call.Return(run)
1836- return _c
1837-}
1838-
1839 // InternalSchemePath provides a mock function for the type MockEngine1991 // InternalSchemePath provides a mock function for the type MockEngine
1840 func (_mock *MockEngine) InternalSchemePath() string {1992 func (_mock *MockEngine) InternalSchemePath() string {
1841 ret := _mock.Called()1993 ret := _mock.Called()
@@ -4339,13 +4491,13 @@ func (_c *MockFocusedInputProvider_SetFocusedInput_Call) RunAndReturn(run func(t
4339 return _c4491 return _c
4340 }4492 }
4341 4493
4342-// NewMockMemoryPressureApplier creates a new instance of MockMemoryPressureApplier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.4494+// NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
4343 // The first argument is typically a *testing.T value.4495 // The first argument is typically a *testing.T value.
4344-func NewMockMemoryPressureApplier(t interface {4496+func NewMockWebView(t interface {
4345 mock.TestingT4497 mock.TestingT
4346 Cleanup(func())4498 Cleanup(func())
4347-}) *MockMemoryPressureApplier {4499+}) *MockWebView {
4348- mock := &MockMemoryPressureApplier{}4500+ mock := &MockWebView{}
4349 mock.Mock.Test(t)4501 mock.Mock.Test(t)
4350 4502
4351 t.Cleanup(func() { mock.AssertExpectations(t) })4503 t.Cleanup(func() { mock.AssertExpectations(t) })
@@ -4353,588 +4505,270 @@ func NewMockMemoryPressureApplier(t interface {
4353 return mock4505 return mock
4354 }4506 }
4355 4507
4356-// MockMemoryPressureApplier is an autogenerated mock type for the MemoryPressureApplier type4508+// MockWebView is an autogenerated mock type for the WebView type
4357-type MockMemoryPressureApplier struct {4509+type MockWebView struct {
4358 mock.Mock4510 mock.Mock
4359 }4511 }
4360 4512
4361-type MockMemoryPressureApplier_Expecter struct {4513+type MockWebView_Expecter struct {
4362 mock *mock.Mock4514 mock *mock.Mock
4363 }4515 }
4364 4516
4365-func (_m *MockMemoryPressureApplier) EXPECT() *MockMemoryPressureApplier_Expecter {4517+func (_m *MockWebView) EXPECT() *MockWebView_Expecter {
4366- return &MockMemoryPressureApplier_Expecter{mock: &_m.Mock}4518+ return &MockWebView_Expecter{mock: &_m.Mock}
4367 }4519 }
4368 4520
4369-// ApplyNetworkProcessSettings provides a mock function for the type MockMemoryPressureApplier4521+// CanGoBack provides a mock function for the type MockWebView
4370-func (_mock *MockMemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error {4522+func (_mock *MockWebView) CanGoBack() bool {
4371- ret := _mock.Called(ctx, cfg)4523+ ret := _mock.Called()
4372 4524
4373 if len(ret) == 0 {4525 if len(ret) == 0 {
4374- panic("no return value specified for ApplyNetworkProcessSettings")4526+ panic("no return value specified for CanGoBack")
4375- }
4376-
4377- var r0 error
4378- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) error); ok {
4379- r0 = returnFunc(ctx, cfg)
4380- } else {
4381- r0 = ret.Error(0)
4382- }
4383- return r0
4384-}
4385-
4386-// MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyNetworkProcessSettings'
4387-type MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call struct {
4388- *mock.Call
4389-}
4390-
4391-// ApplyNetworkProcessSettings is a helper method to define mock.On call
4392-// - ctx context.Context
4393-// - cfg *port.MemoryPressureConfig
4394-func (_e *MockMemoryPressureApplier_Expecter) ApplyNetworkProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4395- return &MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call{Call: _e.mock.On("ApplyNetworkProcessSettings", ctx, cfg)}
4396-}
4397-
4398-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4399- _c.Call.Run(func(args mock.Arguments) {
4400- var arg0 context.Context
4401- if args[0] != nil {
4402- arg0 = args[0].(context.Context)
4403- }
4404- var arg1 *port.MemoryPressureConfig
4405- if args[1] != nil {
4406- arg1 = args[1].(*port.MemoryPressureConfig)
4407- }
4408- run(
4409- arg0,
4410- arg1,
4411- )
4412- })
4413- return _c
4414-}
4415-
4416-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) Return(err error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4417- _c.Call.Return(err)
4418- return _c
4419-}
4420-
4421-func (_c *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) error) *MockMemoryPressureApplier_ApplyNetworkProcessSettings_Call {
4422- _c.Call.Return(run)
4423- return _c
4424-}
4425-
4426-// ApplyWebProcessSettings provides a mock function for the type MockMemoryPressureApplier
4427-func (_mock *MockMemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) {
4428- ret := _mock.Called(ctx, cfg)
4429-
4430- if len(ret) == 0 {
4431- panic("no return value specified for ApplyWebProcessSettings")
4432- }
4433-
4434- var r0 any
4435- var r1 error
4436- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) (any, error)); ok {
4437- return returnFunc(ctx, cfg)
4438- }
4439- if returnFunc, ok := ret.Get(0).(func(context.Context, *port.MemoryPressureConfig) any); ok {
4440- r0 = returnFunc(ctx, cfg)
4441- } else {
4442- if ret.Get(0) != nil {
4443- r0 = ret.Get(0).(any)
4444- }
4445- }
4446- if returnFunc, ok := ret.Get(1).(func(context.Context, *port.MemoryPressureConfig) error); ok {
4447- r1 = returnFunc(ctx, cfg)
4448- } else {
4449- r1 = ret.Error(1)
4450- }
4451- return r0, r1
4452-}
4453-
4454-// MockMemoryPressureApplier_ApplyWebProcessSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyWebProcessSettings'
4455-type MockMemoryPressureApplier_ApplyWebProcessSettings_Call struct {
4456- *mock.Call
4457-}
4458-
4459-// ApplyWebProcessSettings is a helper method to define mock.On call
4460-// - ctx context.Context
4461-// - cfg *port.MemoryPressureConfig
4462-func (_e *MockMemoryPressureApplier_Expecter) ApplyWebProcessSettings(ctx interface{}, cfg interface{}) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4463- return &MockMemoryPressureApplier_ApplyWebProcessSettings_Call{Call: _e.mock.On("ApplyWebProcessSettings", ctx, cfg)}
4464-}
4465-
4466-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Run(run func(ctx context.Context, cfg *port.MemoryPressureConfig)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4467- _c.Call.Run(func(args mock.Arguments) {
4468- var arg0 context.Context
4469- if args[0] != nil {
4470- arg0 = args[0].(context.Context)
4471- }
4472- var arg1 *port.MemoryPressureConfig
4473- if args[1] != nil {
4474- arg1 = args[1].(*port.MemoryPressureConfig)
4475- }
4476- run(
4477- arg0,
4478- arg1,
4479- )
4480- })
4481- return _c
4482-}
4483-
4484-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) Return(v any, err error) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4485- _c.Call.Return(v, err)
4486- return _c
4487-}
4488-
4489-func (_c *MockMemoryPressureApplier_ApplyWebProcessSettings_Call) RunAndReturn(run func(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error)) *MockMemoryPressureApplier_ApplyWebProcessSettings_Call {
4490- _c.Call.Return(run)
4491- return _c
4492-}
4493-
4494-// NewMockWebKitContextProvider creates a new instance of MockWebKitContextProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
4495-// The first argument is typically a *testing.T value.
4496-func NewMockWebKitContextProvider(t interface {
4497- mock.TestingT
4498- Cleanup(func())
4499-}) *MockWebKitContextProvider {
4500- mock := &MockWebKitContextProvider{}
4501- mock.Mock.Test(t)
4502-
4503- t.Cleanup(func() { mock.AssertExpectations(t) })
4504-
4505- return mock
4506-}
4507-
4508-// MockWebKitContextProvider is an autogenerated mock type for the WebKitContextProvider type
4509-type MockWebKitContextProvider struct {
4510- mock.Mock
4511-}
4512-
4513-type MockWebKitContextProvider_Expecter struct {
4514- mock *mock.Mock
4515-}
4516-
4517-func (_m *MockWebKitContextProvider) EXPECT() *MockWebKitContextProvider_Expecter {
4518- return &MockWebKitContextProvider_Expecter{mock: &_m.Mock}
4519-}
4520-
4521-// CacheDir provides a mock function for the type MockWebKitContextProvider
4522-func (_mock *MockWebKitContextProvider) CacheDir() string {
4523- ret := _mock.Called()
4524-
4525- if len(ret) == 0 {
4526- panic("no return value specified for CacheDir")
4527- }
4528-
4529- var r0 string
4530- if returnFunc, ok := ret.Get(0).(func() string); ok {
4531- r0 = returnFunc()
4532- } else {
4533- r0 = ret.Get(0).(string)
4534- }
4535- return r0
4536-}
4537-
4538-// MockWebKitContextProvider_CacheDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CacheDir'
4539-type MockWebKitContextProvider_CacheDir_Call struct {
4540- *mock.Call
4541-}
4542-
4543-// CacheDir is a helper method to define mock.On call
4544-func (_e *MockWebKitContextProvider_Expecter) CacheDir() *MockWebKitContextProvider_CacheDir_Call {
4545- return &MockWebKitContextProvider_CacheDir_Call{Call: _e.mock.On("CacheDir")}
4546-}
4547-
4548-func (_c *MockWebKitContextProvider_CacheDir_Call) Run(run func()) *MockWebKitContextProvider_CacheDir_Call {
4549- _c.Call.Run(func(args mock.Arguments) {
4550- run()
4551- })
4552- return _c
4553-}
4554-
4555-func (_c *MockWebKitContextProvider_CacheDir_Call) Return(s string) *MockWebKitContextProvider_CacheDir_Call {
4556- _c.Call.Return(s)
4557- return _c
4558-}
4559-
4560-func (_c *MockWebKitContextProvider_CacheDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_CacheDir_Call {
4561- _c.Call.Return(run)
4562- return _c
4563-}
4564-
4565-// Close provides a mock function for the type MockWebKitContextProvider
4566-func (_mock *MockWebKitContextProvider) Close() error {
4567- ret := _mock.Called()
4568-
4569- if len(ret) == 0 {
4570- panic("no return value specified for Close")
4571 }4527 }
4572 4528
4573- var r0 error4529+ var r0 bool
4574- if returnFunc, ok := ret.Get(0).(func() error); ok {4530+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
4575 r0 = returnFunc()4531 r0 = returnFunc()
4576 } else {4532 } else {
4577- r0 = ret.Error(0)4533+ r0 = ret.Get(0).(bool)
4578 }4534 }
4579 return r04535 return r0
4580 }4536 }
4581 4537
4582-// MockWebKitContextProvider_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'4538+// MockWebView_CanGoBack_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoBack'
4583-type MockWebKitContextProvider_Close_Call struct {4539+type MockWebView_CanGoBack_Call struct {
4584 *mock.Call4540 *mock.Call
4585 }4541 }
4586 4542
4587-// Close is a helper method to define mock.On call4543+// CanGoBack is a helper method to define mock.On call
4588-func (_e *MockWebKitContextProvider_Expecter) Close() *MockWebKitContextProvider_Close_Call {4544+func (_e *MockWebView_Expecter) CanGoBack() *MockWebView_CanGoBack_Call {
4589- return &MockWebKitContextProvider_Close_Call{Call: _e.mock.On("Close")}4545+ return &MockWebView_CanGoBack_Call{Call: _e.mock.On("CanGoBack")}
4590 }4546 }
4591 4547
4592-func (_c *MockWebKitContextProvider_Close_Call) Run(run func()) *MockWebKitContextProvider_Close_Call {4548+func (_c *MockWebView_CanGoBack_Call) Run(run func()) *MockWebView_CanGoBack_Call {
4593 _c.Call.Run(func(args mock.Arguments) {4549 _c.Call.Run(func(args mock.Arguments) {
4594 run()4550 run()
4595 })4551 })
4596 return _c4552 return _c
4597 }4553 }
4598 4554
4599-func (_c *MockWebKitContextProvider_Close_Call) Return(err error) *MockWebKitContextProvider_Close_Call {4555+func (_c *MockWebView_CanGoBack_Call) Return(b bool) *MockWebView_CanGoBack_Call {
4600- _c.Call.Return(err)4556+ _c.Call.Return(b)
4601 return _c4557 return _c
4602 }4558 }
4603 4559
4604-func (_c *MockWebKitContextProvider_Close_Call) RunAndReturn(run func() error) *MockWebKitContextProvider_Close_Call {4560+func (_c *MockWebView_CanGoBack_Call) RunAndReturn(run func() bool) *MockWebView_CanGoBack_Call {
4605 _c.Call.Return(run)4561 _c.Call.Return(run)
4606 return _c4562 return _c
4607 }4563 }
4608 4564
4609-// DataDir provides a mock function for the type MockWebKitContextProvider4565+// CanGoForward provides a mock function for the type MockWebView
4610-func (_mock *MockWebKitContextProvider) DataDir() string {4566+func (_mock *MockWebView) CanGoForward() bool {
4611 ret := _mock.Called()4567 ret := _mock.Called()
4612 4568
4613 if len(ret) == 0 {4569 if len(ret) == 0 {
4614- panic("no return value specified for DataDir")4570+ panic("no return value specified for CanGoForward")
4615 }4571 }
4616 4572
4617- var r0 string4573+ var r0 bool
4618- if returnFunc, ok := ret.Get(0).(func() string); ok {4574+ if returnFunc, ok := ret.Get(0).(func() bool); ok {
4619 r0 = returnFunc()4575 r0 = returnFunc()
4620 } else {4576 } else {
4621- r0 = ret.Get(0).(string)4577+ r0 = ret.Get(0).(bool)
4622 }4578 }
4623 return r04579 return r0
4624 }4580 }
4625 4581
4626-// MockWebKitContextProvider_DataDir_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DataDir'4582+// MockWebView_CanGoForward_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoForward'
4627-type MockWebKitContextProvider_DataDir_Call struct {4583+type MockWebView_CanGoForward_Call struct {
4628 *mock.Call4584 *mock.Call
4629 }4585 }
4630 4586
4631-// DataDir is a helper method to define mock.On call4587+// CanGoForward is a helper method to define mock.On call
4632-func (_e *MockWebKitContextProvider_Expecter) DataDir() *MockWebKitContextProvider_DataDir_Call {4588+func (_e *MockWebView_Expecter) CanGoForward() *MockWebView_CanGoForward_Call {
4633- return &MockWebKitContextProvider_DataDir_Call{Call: _e.mock.On("DataDir")}4589+ return &MockWebView_CanGoForward_Call{Call: _e.mock.On("CanGoForward")}
4634 }4590 }
4635 4591
4636-func (_c *MockWebKitContextProvider_DataDir_Call) Run(run func()) *MockWebKitContextProvider_DataDir_Call {4592+func (_c *MockWebView_CanGoForward_Call) Run(run func()) *MockWebView_CanGoForward_Call {
4637 _c.Call.Run(func(args mock.Arguments) {4593 _c.Call.Run(func(args mock.Arguments) {
4638 run()4594 run()
4639 })4595 })
4640 return _c4596 return _c
4641 }4597 }
4642 4598
4643-func (_c *MockWebKitContextProvider_DataDir_Call) Return(s string) *MockWebKitContextProvider_DataDir_Call {4599+func (_c *MockWebView_CanGoForward_Call) Return(b bool) *MockWebView_CanGoForward_Call {
4644- _c.Call.Return(s)4600+ _c.Call.Return(b)
4645- return _c
4646-}
4647-
4648-func (_c *MockWebKitContextProvider_DataDir_Call) RunAndReturn(run func() string) *MockWebKitContextProvider_DataDir_Call {
4649- _c.Call.Return(run)
4650- return _c
4651-}
4652-
4653-// Initialize provides a mock function for the type MockWebKitContextProvider
4654-func (_mock *MockWebKitContextProvider) Initialize(ctx context.Context, opts port.WebKitContextOptions) error {
4655- ret := _mock.Called(ctx, opts)
4656-
4657- if len(ret) == 0 {
4658- panic("no return value specified for Initialize")
4659- }
4660-
4661- var r0 error
4662- if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebKitContextOptions) error); ok {
4663- r0 = returnFunc(ctx, opts)
4664- } else {
4665- r0 = ret.Error(0)
4666- }
4667- return r0
4668-}
4669-
4670-// MockWebKitContextProvider_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize'
4671-type MockWebKitContextProvider_Initialize_Call struct {
4672- *mock.Call
4673-}
4674-
4675-// Initialize is a helper method to define mock.On call
4676-// - ctx context.Context
4677-// - opts port.WebKitContextOptions
4678-func (_e *MockWebKitContextProvider_Expecter) Initialize(ctx interface{}, opts interface{}) *MockWebKitContextProvider_Initialize_Call {
4679- return &MockWebKitContextProvider_Initialize_Call{Call: _e.mock.On("Initialize", ctx, opts)}
4680-}
4681-
4682-func (_c *MockWebKitContextProvider_Initialize_Call) Run(run func(ctx context.Context, opts port.WebKitContextOptions)) *MockWebKitContextProvider_Initialize_Call {
4683- _c.Call.Run(func(args mock.Arguments) {
4684- var arg0 context.Context
4685- if args[0] != nil {
4686- arg0 = args[0].(context.Context)
4687- }
4688- var arg1 port.WebKitContextOptions
4689- if args[1] != nil {
4690- arg1 = args[1].(port.WebKitContextOptions)
4691- }
4692- run(
4693- arg0,
4694- arg1,
4695- )
4696- })
4697- return _c
4698-}
4699-
4700-func (_c *MockWebKitContextProvider_Initialize_Call) Return(err error) *MockWebKitContextProvider_Initialize_Call {
4701- _c.Call.Return(err)
4702 return _c4601 return _c
4703 }4602 }
4704 4603
4705-func (_c *MockWebKitContextProvider_Initialize_Call) RunAndReturn(run func(ctx context.Context, opts port.WebKitContextOptions) error) *MockWebKitContextProvider_Initialize_Call {4604+func (_c *MockWebView_CanGoForward_Call) RunAndReturn(run func() bool) *MockWebView_CanGoForward_Call {
4706 _c.Call.Return(run)4605 _c.Call.Return(run)
4707 return _c4606 return _c
4708 }4607 }
4709 4608
4710-// PrefetchDNS provides a mock function for the type MockWebKitContextProvider4609+// Destroy provides a mock function for the type MockWebView
4711-func (_mock *MockWebKitContextProvider) PrefetchDNS(hostname string) {4610+func (_mock *MockWebView) Destroy() {
4712- _mock.Called(hostname)4611+ _mock.Called()
4713 return4612 return
4714 }4613 }
4715 4614
4716-// MockWebKitContextProvider_PrefetchDNS_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrefetchDNS'4615+// MockWebView_Destroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destroy'
4717-type MockWebKitContextProvider_PrefetchDNS_Call struct {4616+type MockWebView_Destroy_Call struct {
4718 *mock.Call4617 *mock.Call
4719 }4618 }
4720 4619
4721-// PrefetchDNS is a helper method to define mock.On call4620+// Destroy is a helper method to define mock.On call
4722-// - hostname string4621+func (_e *MockWebView_Expecter) Destroy() *MockWebView_Destroy_Call {
4723-func (_e *MockWebKitContextProvider_Expecter) PrefetchDNS(hostname interface{}) *MockWebKitContextProvider_PrefetchDNS_Call {4622+ return &MockWebView_Destroy_Call{Call: _e.mock.On("Destroy")}
4724- return &MockWebKitContextProvider_PrefetchDNS_Call{Call: _e.mock.On("PrefetchDNS", hostname)}
4725 }4623 }
4726 4624
4727-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Run(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call {4625+func (_c *MockWebView_Destroy_Call) Run(run func()) *MockWebView_Destroy_Call {
4728 _c.Call.Run(func(args mock.Arguments) {4626 _c.Call.Run(func(args mock.Arguments) {
4729- var arg0 string4627+ run()
4730- if args[0] != nil {
4731- arg0 = args[0].(string)
4732- }
4733- run(
4734- arg0,
4735- )
4736 })4628 })
4737 return _c4629 return _c
4738 }4630 }
4739 4631
4740-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) Return() *MockWebKitContextProvider_PrefetchDNS_Call {4632+func (_c *MockWebView_Destroy_Call) Return() *MockWebView_Destroy_Call {
4741 _c.Call.Return()4633 _c.Call.Return()
4742 return _c4634 return _c
4743 }4635 }
4744 4636
4745-func (_c *MockWebKitContextProvider_PrefetchDNS_Call) RunAndReturn(run func(hostname string)) *MockWebKitContextProvider_PrefetchDNS_Call {4637+func (_c *MockWebView_Destroy_Call) RunAndReturn(run func()) *MockWebView_Destroy_Call {
4746 _c.Run(run)4638 _c.Run(run)
4747 return _c4639 return _c
4748 }4640 }
4749 4641
4750-// NewMockWebView creates a new instance of MockWebView. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.4642+// EstimatedProgress provides a mock function for the type MockWebView
4751-// The first argument is typically a *testing.T value.4643+func (_mock *MockWebView) EstimatedProgress() float64 {
4752-func NewMockWebView(t interface {
4753- mock.TestingT
4754- Cleanup(func())
4755-}) *MockWebView {
4756- mock := &MockWebView{}
4757- mock.Mock.Test(t)
4758-
4759- t.Cleanup(func() { mock.AssertExpectations(t) })
4760-
4761- return mock
4762-}
4763-
4764-// MockWebView is an autogenerated mock type for the WebView type
4765-type MockWebView struct {
4766- mock.Mock
4767-}
4768-
4769-type MockWebView_Expecter struct {
4770- mock *mock.Mock
4771-}
4772-
4773-func (_m *MockWebView) EXPECT() *MockWebView_Expecter {
4774- return &MockWebView_Expecter{mock: &_m.Mock}
4775-}
4776-
4777-// CanGoBack provides a mock function for the type MockWebView
4778-func (_mock *MockWebView) CanGoBack() bool {
4779 ret := _mock.Called()4644 ret := _mock.Called()
4780 4645
4781 if len(ret) == 0 {4646 if len(ret) == 0 {
4782- panic("no return value specified for CanGoBack")4647+ panic("no return value specified for EstimatedProgress")
4783 }4648 }
4784 4649
4785- var r0 bool4650+ var r0 float64
4786- if returnFunc, ok := ret.Get(0).(func() bool); ok {4651+ if returnFunc, ok := ret.Get(0).(func() float64); ok {
4787 r0 = returnFunc()4652 r0 = returnFunc()
4788 } else {4653 } else {
4789- r0 = ret.Get(0).(bool)4654+ r0 = ret.Get(0).(float64)
4790 }4655 }
4791 return r04656 return r0
4792 }4657 }
4793 4658
4794-// MockWebView_CanGoBack_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoBack'4659+// MockWebView_EstimatedProgress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimatedProgress'
4795-type MockWebView_CanGoBack_Call struct {4660+type MockWebView_EstimatedProgress_Call struct {
4796 *mock.Call4661 *mock.Call
4797 }4662 }
4798 4663
4799-// CanGoBack is a helper method to define mock.On call4664+// EstimatedProgress is a helper method to define mock.On call
4800-func (_e *MockWebView_Expecter) CanGoBack() *MockWebView_CanGoBack_Call {4665+func (_e *MockWebView_Expecter) EstimatedProgress() *MockWebView_EstimatedProgress_Call {
4801- return &MockWebView_CanGoBack_Call{Call: _e.mock.On("CanGoBack")}4666+ return &MockWebView_EstimatedProgress_Call{Call: _e.mock.On("EstimatedProgress")}
4802 }4667 }
4803 4668
4804-func (_c *MockWebView_CanGoBack_Call) Run(run func()) *MockWebView_CanGoBack_Call {4669+func (_c *MockWebView_EstimatedProgress_Call) Run(run func()) *MockWebView_EstimatedProgress_Call {
4805 _c.Call.Run(func(args mock.Arguments) {4670 _c.Call.Run(func(args mock.Arguments) {
4806 run()4671 run()
4807 })4672 })
4808 return _c4673 return _c
4809 }4674 }
4810 4675
4811-func (_c *MockWebView_CanGoBack_Call) Return(b bool) *MockWebView_CanGoBack_Call {4676+func (_c *MockWebView_EstimatedProgress_Call) Return(f float64) *MockWebView_EstimatedProgress_Call {
4812- _c.Call.Return(b)4677+ _c.Call.Return(f)
4813 return _c4678 return _c
4814 }4679 }
4815 4680
4816-func (_c *MockWebView_CanGoBack_Call) RunAndReturn(run func() bool) *MockWebView_CanGoBack_Call {4681+func (_c *MockWebView_EstimatedProgress_Call) RunAndReturn(run func() float64) *MockWebView_EstimatedProgress_Call {
4817 _c.Call.Return(run)4682 _c.Call.Return(run)
4818 return _c4683 return _c
4819 }4684 }
4820 4685
4821-// CanGoForward provides a mock function for the type MockWebView4686+// Favicon provides a mock function for the type MockWebView
4822-func (_mock *MockWebView) CanGoForward() bool {4687+func (_mock *MockWebView) Favicon() port.Texture {
4823 ret := _mock.Called()4688 ret := _mock.Called()
4824 4689
4825 if len(ret) == 0 {4690 if len(ret) == 0 {
4826- panic("no return value specified for CanGoForward")4691+ panic("no return value specified for Favicon")
4827 }4692 }
4828 4693
4829- var r0 bool4694+ var r0 port.Texture
4830- if returnFunc, ok := ret.Get(0).(func() bool); ok {4695+ if returnFunc, ok := ret.Get(0).(func() port.Texture); ok {
4831 r0 = returnFunc()4696 r0 = returnFunc()
4832 } else {4697 } else {
4833- r0 = ret.Get(0).(bool)4698+ if ret.Get(0) != nil {
4699+ r0 = ret.Get(0).(port.Texture)
4700+ }
4834 }4701 }
4835 return r04702 return r0
4836 }4703 }
4837 4704
4838-// MockWebView_CanGoForward_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CanGoForward'4705+// MockWebView_Favicon_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Favicon'
4839-type MockWebView_CanGoForward_Call struct {4706+type MockWebView_Favicon_Call struct {
4840 *mock.Call4707 *mock.Call
4841 }4708 }
4842 4709
4843-// CanGoForward is a helper method to define mock.On call4710+// Favicon is a helper method to define mock.On call
4844-func (_e *MockWebView_Expecter) CanGoForward() *MockWebView_CanGoForward_Call {4711+func (_e *MockWebView_Expecter) Favicon() *MockWebView_Favicon_Call {
4845- return &MockWebView_CanGoForward_Call{Call: _e.mock.On("CanGoForward")}4712+ return &MockWebView_Favicon_Call{Call: _e.mock.On("Favicon")}
4846 }4713 }
4847 4714
4848-func (_c *MockWebView_CanGoForward_Call) Run(run func()) *MockWebView_CanGoForward_Call {4715+func (_c *MockWebView_Favicon_Call) Run(run func()) *MockWebView_Favicon_Call {
4849 _c.Call.Run(func(args mock.Arguments) {4716 _c.Call.Run(func(args mock.Arguments) {
4850 run()4717 run()
4851 })4718 })
4852 return _c4719 return _c
4853 }4720 }
4854 4721
4855-func (_c *MockWebView_CanGoForward_Call) Return(b bool) *MockWebView_CanGoForward_Call {4722+func (_c *MockWebView_Favicon_Call) Return(texture port.Texture) *MockWebView_Favicon_Call {
4856- _c.Call.Return(b)4723+ _c.Call.Return(texture)
4857 return _c4724 return _c
4858 }4725 }
4859 4726
4860-func (_c *MockWebView_CanGoForward_Call) RunAndReturn(run func() bool) *MockWebView_CanGoForward_Call {4727+func (_c *MockWebView_Favicon_Call) RunAndReturn(run func() port.Texture) *MockWebView_Favicon_Call {
4861 _c.Call.Return(run)4728 _c.Call.Return(run)
4862 return _c4729 return _c
4863 }4730 }
4864 4731
4865-// Destroy provides a mock function for the type MockWebView4732+// Generation provides a mock function for the type MockWebView
4866-func (_mock *MockWebView) Destroy() {4733+func (_mock *MockWebView) Generation() uint64 {
4867- _mock.Called()
4868- return
4869-}
4870-
4871-// MockWebView_Destroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destroy'
4872-type MockWebView_Destroy_Call struct {
4873- *mock.Call
4874-}
4875-
4876-// Destroy is a helper method to define mock.On call
4877-func (_e *MockWebView_Expecter) Destroy() *MockWebView_Destroy_Call {
4878- return &MockWebView_Destroy_Call{Call: _e.mock.On("Destroy")}
4879-}
4880-
4881-func (_c *MockWebView_Destroy_Call) Run(run func()) *MockWebView_Destroy_Call {
4882- _c.Call.Run(func(args mock.Arguments) {
4883- run()
4884- })
4885- return _c
4886-}
4887-
4888-func (_c *MockWebView_Destroy_Call) Return() *MockWebView_Destroy_Call {
4889- _c.Call.Return()
4890- return _c
4891-}
4892-
4893-func (_c *MockWebView_Destroy_Call) RunAndReturn(run func()) *MockWebView_Destroy_Call {
4894- _c.Run(run)
4895- return _c
4896-}
4897-
4898-// EstimatedProgress provides a mock function for the type MockWebView
4899-func (_mock *MockWebView) EstimatedProgress() float64 {
4900 ret := _mock.Called()4734 ret := _mock.Called()
4901 4735
4902 if len(ret) == 0 {4736 if len(ret) == 0 {
4903- panic("no return value specified for EstimatedProgress")4737+ panic("no return value specified for Generation")
4904 }4738 }
4905 4739
4906- var r0 float644740+ var r0 uint64
4907- if returnFunc, ok := ret.Get(0).(func() float64); ok {4741+ if returnFunc, ok := ret.Get(0).(func() uint64); ok {
4908 r0 = returnFunc()4742 r0 = returnFunc()
4909 } else {4743 } else {
4910- r0 = ret.Get(0).(float64)4744+ r0 = ret.Get(0).(uint64)
4911 }4745 }
4912 return r04746 return r0
4913 }4747 }
4914 4748
4915-// MockWebView_EstimatedProgress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimatedProgress'4749+// MockWebView_Generation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Generation'
4916-type MockWebView_EstimatedProgress_Call struct {4750+type MockWebView_Generation_Call struct {
4917 *mock.Call4751 *mock.Call
4918 }4752 }
4919 4753
4920-// EstimatedProgress is a helper method to define mock.On call4754+// Generation is a helper method to define mock.On call
4921-func (_e *MockWebView_Expecter) EstimatedProgress() *MockWebView_EstimatedProgress_Call {4755+func (_e *MockWebView_Expecter) Generation() *MockWebView_Generation_Call {
4922- return &MockWebView_EstimatedProgress_Call{Call: _e.mock.On("EstimatedProgress")}4756+ return &MockWebView_Generation_Call{Call: _e.mock.On("Generation")}
4923 }4757 }
4924 4758
4925-func (_c *MockWebView_EstimatedProgress_Call) Run(run func()) *MockWebView_EstimatedProgress_Call {4759+func (_c *MockWebView_Generation_Call) Run(run func()) *MockWebView_Generation_Call {
4926 _c.Call.Run(func(args mock.Arguments) {4760 _c.Call.Run(func(args mock.Arguments) {
4927 run()4761 run()
4928 })4762 })
4929 return _c4763 return _c
4930 }4764 }
4931 4765
4932-func (_c *MockWebView_EstimatedProgress_Call) Return(f float64) *MockWebView_EstimatedProgress_Call {4766+func (_c *MockWebView_Generation_Call) Return(v uint64) *MockWebView_Generation_Call {
4933- _c.Call.Return(f)4767+ _c.Call.Return(v)
4934 return _c4768 return _c
4935 }4769 }
4936 4770
4937-func (_c *MockWebView_EstimatedProgress_Call) RunAndReturn(run func() float64) *MockWebView_EstimatedProgress_Call {4771+func (_c *MockWebView_Generation_Call) RunAndReturn(run func() uint64) *MockWebView_Generation_Call {
4938 _c.Call.Return(run)4772 _c.Call.Return(run)
4939 return _c4773 return _c
4940 }4774 }
@@ -5573,6 +5407,143 @@ func (_c *MockWebView_ReloadBypassCache_Call) RunAndReturn(run func(ctx context.
5573 return _c5407 return _c
5574 }5408 }
5575 5409
5410+// ResetBackgroundToDefault provides a mock function for the type MockWebView
5411+func (_mock *MockWebView) ResetBackgroundToDefault() {
5412+ _mock.Called()
5413+ return
5414+}
5415+
5416+// MockWebView_ResetBackgroundToDefault_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResetBackgroundToDefault'
5417+type MockWebView_ResetBackgroundToDefault_Call struct {
5418+ *mock.Call
5419+}
5420+
5421+// ResetBackgroundToDefault is a helper method to define mock.On call
5422+func (_e *MockWebView_Expecter) ResetBackgroundToDefault() *MockWebView_ResetBackgroundToDefault_Call {
5423+ return &MockWebView_ResetBackgroundToDefault_Call{Call: _e.mock.On("ResetBackgroundToDefault")}
5424+}
5425+
5426+func (_c *MockWebView_ResetBackgroundToDefault_Call) Run(run func()) *MockWebView_ResetBackgroundToDefault_Call {
5427+ _c.Call.Run(func(args mock.Arguments) {
5428+ run()
5429+ })
5430+ return _c
5431+}
5432+
5433+func (_c *MockWebView_ResetBackgroundToDefault_Call) Return() *MockWebView_ResetBackgroundToDefault_Call {
5434+ _c.Call.Return()
5435+ return _c
5436+}
5437+
5438+func (_c *MockWebView_ResetBackgroundToDefault_Call) RunAndReturn(run func()) *MockWebView_ResetBackgroundToDefault_Call {
5439+ _c.Run(run)
5440+ return _c
5441+}
5442+
5443+// RunJavaScript provides a mock function for the type MockWebView
5444+func (_mock *MockWebView) RunJavaScript(ctx context.Context, script string) {
5445+ _mock.Called(ctx, script)
5446+ return
5447+}
5448+
5449+// MockWebView_RunJavaScript_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RunJavaScript'
5450+type MockWebView_RunJavaScript_Call struct {
5451+ *mock.Call
5452+}
5453+
5454+// RunJavaScript is a helper method to define mock.On call
5455+// - ctx context.Context
5456+// - script string
5457+func (_e *MockWebView_Expecter) RunJavaScript(ctx interface{}, script interface{}) *MockWebView_RunJavaScript_Call {
5458+ return &MockWebView_RunJavaScript_Call{Call: _e.mock.On("RunJavaScript", ctx, script)}
5459+}
5460+
5461+func (_c *MockWebView_RunJavaScript_Call) Run(run func(ctx context.Context, script string)) *MockWebView_RunJavaScript_Call {
5462+ _c.Call.Run(func(args mock.Arguments) {
5463+ var arg0 context.Context
5464+ if args[0] != nil {
5465+ arg0 = args[0].(context.Context)
5466+ }
5467+ var arg1 string
5468+ if args[1] != nil {
5469+ arg1 = args[1].(string)
5470+ }
5471+ run(
5472+ arg0,
5473+ arg1,
5474+ )
5475+ })
5476+ return _c
5477+}
5478+
5479+func (_c *MockWebView_RunJavaScript_Call) Return() *MockWebView_RunJavaScript_Call {
5480+ _c.Call.Return()
5481+ return _c
5482+}
5483+
5484+func (_c *MockWebView_RunJavaScript_Call) RunAndReturn(run func(ctx context.Context, script string)) *MockWebView_RunJavaScript_Call {
5485+ _c.Run(run)
5486+ return _c
5487+}
5488+
5489+// SetBackgroundColor provides a mock function for the type MockWebView
5490+func (_mock *MockWebView) SetBackgroundColor(r float64, g float64, b float64, a float64) {
5491+ _mock.Called(r, g, b, a)
5492+ return
5493+}
5494+
5495+// MockWebView_SetBackgroundColor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetBackgroundColor'
5496+type MockWebView_SetBackgroundColor_Call struct {
5497+ *mock.Call
5498+}
5499+
5500+// SetBackgroundColor is a helper method to define mock.On call
5501+// - r float64
5502+// - g float64
5503+// - b float64
5504+// - a float64
5505+func (_e *MockWebView_Expecter) SetBackgroundColor(r interface{}, g interface{}, b interface{}, a interface{}) *MockWebView_SetBackgroundColor_Call {
5506+ return &MockWebView_SetBackgroundColor_Call{Call: _e.mock.On("SetBackgroundColor", r, g, b, a)}
5507+}
5508+
5509+func (_c *MockWebView_SetBackgroundColor_Call) Run(run func(r float64, g float64, b float64, a float64)) *MockWebView_SetBackgroundColor_Call {
5510+ _c.Call.Run(func(args mock.Arguments) {
5511+ var arg0 float64
5512+ if args[0] != nil {
5513+ arg0 = args[0].(float64)
5514+ }
5515+ var arg1 float64
5516+ if args[1] != nil {
5517+ arg1 = args[1].(float64)
5518+ }
5519+ var arg2 float64
5520+ if args[2] != nil {
5521+ arg2 = args[2].(float64)
5522+ }
5523+ var arg3 float64
5524+ if args[3] != nil {
5525+ arg3 = args[3].(float64)
5526+ }
5527+ run(
5528+ arg0,
5529+ arg1,
5530+ arg2,
5531+ arg3,
5532+ )
5533+ })
5534+ return _c
5535+}
5536+
5537+func (_c *MockWebView_SetBackgroundColor_Call) Return() *MockWebView_SetBackgroundColor_Call {
5538+ _c.Call.Return()
5539+ return _c
5540+}
5541+
5542+func (_c *MockWebView_SetBackgroundColor_Call) RunAndReturn(run func(r float64, g float64, b float64, a float64)) *MockWebView_SetBackgroundColor_Call {
5543+ _c.Run(run)
5544+ return _c
5545+}
5546+
5576 // SetCallbacks provides a mock function for the type MockWebView5547 // SetCallbacks provides a mock function for the type MockWebView
5577 func (_mock *MockWebView) SetCallbacks(callbacks *port.WebViewCallbacks) {5548 func (_mock *MockWebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
5578 _mock.Called(callbacks)5549 _mock.Called(callbacks)
D internal/application/port/webkit_context.go +0 -119
@@ -1,119 +0,0 @@
1-// Package port defines interfaces for external dependencies.
2-package port
3-
4-import "context"
5-
6-// WebKitCookiePolicy controls cookie acceptance behavior for NetworkSession.
7-type WebKitCookiePolicy string
8-
9-const (
10- WebKitCookiePolicyAlways WebKitCookiePolicy = "always"
11- WebKitCookiePolicyNoThirdParty WebKitCookiePolicy = "no_third_party"
12- WebKitCookiePolicyNever WebKitCookiePolicy = "never"
13-)
14-
15-// MemoryPressureConfig holds memory pressure settings for a WebKit process.
16-// Zero values mean "use WebKit defaults".
17-type MemoryPressureConfig struct {
18- // MemoryLimitMB sets the memory limit in megabytes.
19- // 0 means unset (uses WebKit default: system RAM capped at 3GB).
20- MemoryLimitMB int
21-
22- // PollIntervalSec sets the interval for memory usage checks.
23- // 0 means unset (uses WebKit default: 30 seconds).
24- PollIntervalSec float64
25-
26- // ConservativeThreshold sets threshold for conservative memory release.
27- // Must be in (0, 1). 0 means unset (uses WebKit default: 0.33).
28- ConservativeThreshold float64
29-
30- // StrictThreshold sets threshold for strict memory release.
31- // Must be in (0, 1). 0 means unset (uses WebKit default: 0.5).
32- StrictThreshold float64
33-}
34-
35-// IsConfigured returns true if any memory pressure setting is configured.
36-func (c *MemoryPressureConfig) IsConfigured() bool {
37- if c == nil {
38- return false
39- }
40- return c.MemoryLimitMB > 0 ||
41- c.PollIntervalSec > 0 ||
42- c.ConservativeThreshold > 0 ||
43- c.StrictThreshold > 0
44-}
45-
46-// WebKitContextOptions configures WebKitContext creation.
47-type WebKitContextOptions struct {
48- // DataDir is the directory for persistent data (cookies, storage, etc.).
49- DataDir string
50-
51- // CacheDir is the directory for cache data.
52- CacheDir string
53-
54- // CookiePolicy controls cookie acceptance behavior.
55- // Empty value means runtime defaults are used.
56- CookiePolicy WebKitCookiePolicy
57-
58- // ITPEnabled controls Intelligent Tracking Prevention for NetworkSession.
59- // False means ITP remains disabled unless enabled explicitly.
60- ITPEnabled bool
61-
62- // WebProcessMemory configures memory pressure for web processes.
63- // nil means use WebKit defaults.
64- WebProcessMemory *MemoryPressureConfig
65-
66- // NetworkProcessMemory configures memory pressure for the network process.
67- // nil means use WebKit defaults.
68- NetworkProcessMemory *MemoryPressureConfig
69-}
70-
71-// IsWebProcessMemoryConfigured returns true if web process memory settings are configured.
72-func (o *WebKitContextOptions) IsWebProcessMemoryConfigured() bool {
73- if o == nil {
74- return false
75- }
76- return o.WebProcessMemory.IsConfigured()
77-}
78-
79-// IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured.
80-func (o *WebKitContextOptions) IsNetworkProcessMemoryConfigured() bool {
81- if o == nil {
82- return false
83- }
84- return o.NetworkProcessMemory.IsConfigured()
85-}
86-
87-// MemoryPressureApplier applies memory pressure settings to WebKit processes.
88-// This interface abstracts the WebKit-specific memory pressure configuration
89-// to allow testing without WebKit dependencies.
90-type MemoryPressureApplier interface {
91- // ApplyNetworkProcessSettings applies memory pressure settings to the network process.
92- // Must be called BEFORE creating any NetworkSession.
93- ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error
94-
95- // ApplyWebProcessSettings applies memory pressure settings to web processes.
96- // Returns an opaque settings object that should be passed to WebContext creation.
97- // Returns nil if no settings are configured.
98- ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error)
99-}
100-
101-// WebKitContextProvider creates and manages WebKit contexts.
102-// This interface abstracts WebKit context creation for testability.
103-type WebKitContextProvider interface {
104- // Initialize sets up the WebKit context with the given options.
105- // Must be called before any other methods.
106- Initialize(ctx context.Context, opts WebKitContextOptions) error
107-
108- // DataDir returns the data directory path.
109- DataDir() string
110-
111- // CacheDir returns the cache directory path.
112- CacheDir() string
113-
114- // PrefetchDNS prefetches DNS for the given hostname.
115- PrefetchDNS(hostname string)
116-
117- // Close releases resources held by the context.
118- Close() error
119-}
@@ -1,119 +0,0 @@
1-// Package port defines interfaces for external dependencies.
2-package port
3-
4-import "context"
5-
6-// WebKitCookiePolicy controls cookie acceptance behavior for NetworkSession.
7-type WebKitCookiePolicy string
8-
9-const (
10- WebKitCookiePolicyAlways WebKitCookiePolicy = "always"
11- WebKitCookiePolicyNoThirdParty WebKitCookiePolicy = "no_third_party"
12- WebKitCookiePolicyNever WebKitCookiePolicy = "never"
13-)
14-
15-// MemoryPressureConfig holds memory pressure settings for a WebKit process.
16-// Zero values mean "use WebKit defaults".
17-type MemoryPressureConfig struct {
18- // MemoryLimitMB sets the memory limit in megabytes.
19- // 0 means unset (uses WebKit default: system RAM capped at 3GB).
20- MemoryLimitMB int
21-
22- // PollIntervalSec sets the interval for memory usage checks.
23- // 0 means unset (uses WebKit default: 30 seconds).
24- PollIntervalSec float64
25-
26- // ConservativeThreshold sets threshold for conservative memory release.
27- // Must be in (0, 1). 0 means unset (uses WebKit default: 0.33).
28- ConservativeThreshold float64
29-
30- // StrictThreshold sets threshold for strict memory release.
31- // Must be in (0, 1). 0 means unset (uses WebKit default: 0.5).
32- StrictThreshold float64
33-}
34-
35-// IsConfigured returns true if any memory pressure setting is configured.
36-func (c *MemoryPressureConfig) IsConfigured() bool {
37- if c == nil {
38- return false
39- }
40- return c.MemoryLimitMB > 0 ||
41- c.PollIntervalSec > 0 ||
42- c.ConservativeThreshold > 0 ||
43- c.StrictThreshold > 0
44-}
45-
46-// WebKitContextOptions configures WebKitContext creation.
47-type WebKitContextOptions struct {
48- // DataDir is the directory for persistent data (cookies, storage, etc.).
49- DataDir string
50-
51- // CacheDir is the directory for cache data.
52- CacheDir string
53-
54- // CookiePolicy controls cookie acceptance behavior.
55- // Empty value means runtime defaults are used.
56- CookiePolicy WebKitCookiePolicy
57-
58- // ITPEnabled controls Intelligent Tracking Prevention for NetworkSession.
59- // False means ITP remains disabled unless enabled explicitly.
60- ITPEnabled bool
61-
62- // WebProcessMemory configures memory pressure for web processes.
63- // nil means use WebKit defaults.
64- WebProcessMemory *MemoryPressureConfig
65-
66- // NetworkProcessMemory configures memory pressure for the network process.
67- // nil means use WebKit defaults.
68- NetworkProcessMemory *MemoryPressureConfig
69-}
70-
71-// IsWebProcessMemoryConfigured returns true if web process memory settings are configured.
72-func (o *WebKitContextOptions) IsWebProcessMemoryConfigured() bool {
73- if o == nil {
74- return false
75- }
76- return o.WebProcessMemory.IsConfigured()
77-}
78-
79-// IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured.
80-func (o *WebKitContextOptions) IsNetworkProcessMemoryConfigured() bool {
81- if o == nil {
82- return false
83- }
84- return o.NetworkProcessMemory.IsConfigured()
85-}
86-
87-// MemoryPressureApplier applies memory pressure settings to WebKit processes.
88-// This interface abstracts the WebKit-specific memory pressure configuration
89-// to allow testing without WebKit dependencies.
90-type MemoryPressureApplier interface {
91- // ApplyNetworkProcessSettings applies memory pressure settings to the network process.
92- // Must be called BEFORE creating any NetworkSession.
93- ApplyNetworkProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) error
94-
95- // ApplyWebProcessSettings applies memory pressure settings to web processes.
96- // Returns an opaque settings object that should be passed to WebContext creation.
97- // Returns nil if no settings are configured.
98- ApplyWebProcessSettings(ctx context.Context, cfg *MemoryPressureConfig) (any, error)
99-}
100-
101-// WebKitContextProvider creates and manages WebKit contexts.
102-// This interface abstracts WebKit context creation for testability.
103-type WebKitContextProvider interface {
104- // Initialize sets up the WebKit context with the given options.
105- // Must be called before any other methods.
106- Initialize(ctx context.Context, opts WebKitContextOptions) error
107-
108- // DataDir returns the data directory path.
109- DataDir() string
110-
111- // CacheDir returns the cache directory path.
112- CacheDir() string
113-
114- // PrefetchDNS prefetches DNS for the given hostname.
115- PrefetchDNS(hostname string)
116-
117- // Close releases resources held by the context.
118- Close() error
119-}
M internal/application/port/webview.go +35 -0
@@ -109,6 +109,21 @@ type WebViewCallbacks struct {
109109 // Return true to indicate the request was handled. Call allow()/deny() to respond.
110110 // The permission types are passed as strings ("microphone", "camera", "display", etc.).
111111 OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool
112+
113+ // OnLinkMiddleClick is called when a link is middle-clicked.
114+ // Return true if handled (blocks default navigation).
115+ OnLinkMiddleClick func(uri string) bool
116+
117+ // OnEnterFullscreen is called when the WebView requests fullscreen mode.
118+ // Return true to prevent fullscreen.
119+ OnEnterFullscreen func() bool
120+
121+ // OnLeaveFullscreen is called when the WebView exits fullscreen mode.
122+ // Return true to prevent leaving fullscreen.
123+ OnLeaveFullscreen func() bool
124+
125+ // OnAudioStateChanged is called when audio playback starts or stops.
126+ OnAudioStateChanged func(playing bool)
112127 }
113128
114129 // FindOptions configures search behavior.
@@ -210,6 +225,26 @@ type WebView interface {
210225 // Pass nil to clear all callbacks.
211226 SetCallbacks(callbacks *WebViewCallbacks)
212227
228+ // --- Appearance ---
229+
230+ // RunJavaScript executes a script in the main world. Fire-and-forget.
231+ RunJavaScript(ctx context.Context, script string)
232+
233+ // SetBackgroundColor sets the background color shown before content paints (0.0-1.0 RGBA).
234+ SetBackgroundColor(r, g, b, a float64)
235+
236+ // ResetBackgroundToDefault resets the background to white (browser default).
237+ ResetBackgroundToDefault()
238+
239+ // --- Favicon ---
240+
241+ // Favicon returns the current page favicon, or nil if unavailable.
242+ Favicon() Texture
243+
244+ // Generation returns a monotonic counter incremented on pool reuse.
245+ // Used to detect stale callbacks after a WebView is recycled.
246+ Generation() uint64
247+
213248 // --- Media/Fullscreen State ---
214249
215250 // IsFullscreen returns true if the WebView is currently in fullscreen mode.
@@ -109,6 +109,21 @@ type WebViewCallbacks struct {
109 // Return true to indicate the request was handled. Call allow()/deny() to respond.109 // Return true to indicate the request was handled. Call allow()/deny() to respond.
110 // The permission types are passed as strings ("microphone", "camera", "display", etc.).110 // The permission types are passed as strings ("microphone", "camera", "display", etc.).
111 OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool111 OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool
112+
113+ // OnLinkMiddleClick is called when a link is middle-clicked.
114+ // Return true if handled (blocks default navigation).
115+ OnLinkMiddleClick func(uri string) bool
116+
117+ // OnEnterFullscreen is called when the WebView requests fullscreen mode.
118+ // Return true to prevent fullscreen.
119+ OnEnterFullscreen func() bool
120+
121+ // OnLeaveFullscreen is called when the WebView exits fullscreen mode.
122+ // Return true to prevent leaving fullscreen.
123+ OnLeaveFullscreen func() bool
124+
125+ // OnAudioStateChanged is called when audio playback starts or stops.
126+ OnAudioStateChanged func(playing bool)
112 }127 }
113 128
114 // FindOptions configures search behavior.129 // FindOptions configures search behavior.
@@ -210,6 +225,26 @@ type WebView interface {
210 // Pass nil to clear all callbacks.225 // Pass nil to clear all callbacks.
211 SetCallbacks(callbacks *WebViewCallbacks)226 SetCallbacks(callbacks *WebViewCallbacks)
212 227
228+ // --- Appearance ---
229+
230+ // RunJavaScript executes a script in the main world. Fire-and-forget.
231+ RunJavaScript(ctx context.Context, script string)
232+
233+ // SetBackgroundColor sets the background color shown before content paints (0.0-1.0 RGBA).
234+ SetBackgroundColor(r, g, b, a float64)
235+
236+ // ResetBackgroundToDefault resets the background to white (browser default).
237+ ResetBackgroundToDefault()
238+
239+ // --- Favicon ---
240+
241+ // Favicon returns the current page favicon, or nil if unavailable.
242+ Favicon() Texture
243+
244+ // Generation returns a monotonic counter incremented on pool reuse.
245+ // Used to detect stale callbacks after a WebView is recycled.
246+ Generation() uint64
247+
213 // --- Media/Fullscreen State ---248 // --- Media/Fullscreen State ---
214 249
215 // IsFullscreen returns true if the WebView is currently in fullscreen mode.250 // IsFullscreen returns true if the WebView is currently in fullscreen mode.
M internal/bootstrap/gui.go +1 -1
@@ -199,7 +199,7 @@ func CheckRuntimeRequirements(ctx context.Context, cfg *config.Config) error {
199199 probe := deps.NewPkgConfigProbe()
200200 checkRuntimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)
201201 runtimeOut, err := checkRuntimeUC.Execute(ctx, usecase.CheckRuntimeDependenciesInput{
202- Prefix: cfg.Runtime.Prefix,
202+ Prefix: cfg.Engine.WebKit.Prefix,
203203 })
204204 if err != nil {
205205 return fmt.Errorf("runtime check execution failed: %w", err)
@@ -199,7 +199,7 @@ func CheckRuntimeRequirements(ctx context.Context, cfg *config.Config) error {
199 probe := deps.NewPkgConfigProbe()199 probe := deps.NewPkgConfigProbe()
200 checkRuntimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)200 checkRuntimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)
201 runtimeOut, err := checkRuntimeUC.Execute(ctx, usecase.CheckRuntimeDependenciesInput{201 runtimeOut, err := checkRuntimeUC.Execute(ctx, usecase.CheckRuntimeDependenciesInput{
202- Prefix: cfg.Runtime.Prefix,202+ Prefix: cfg.Engine.WebKit.Prefix,
203 })203 })
204 if err != nil {204 if err != nil {
205 return fmt.Errorf("runtime check execution failed: %w", err)205 return fmt.Errorf("runtime check execution failed: %w", err)
M internal/cli/cmd/doctor.go +1 -1
@@ -59,7 +59,7 @@ func runDoctor(_ *cobra.Command, _ []string) error {
5959 probe := deps.NewPkgConfigProbe()
6060 runtimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)
6161 runtimeOut, err := runtimeUC.Execute(app.Ctx(), usecase.CheckRuntimeDependenciesInput{
62- Prefix: app.Config.Runtime.Prefix,
62+ Prefix: app.Config.Engine.WebKit.Prefix,
6363 })
6464 if err != nil {
6565 return err
@@ -59,7 +59,7 @@ func runDoctor(_ *cobra.Command, _ []string) error {
59 probe := deps.NewPkgConfigProbe()59 probe := deps.NewPkgConfigProbe()
60 runtimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)60 runtimeUC := usecase.NewCheckRuntimeDependenciesUseCase(probe)
61 runtimeOut, err := runtimeUC.Execute(app.Ctx(), usecase.CheckRuntimeDependenciesInput{61 runtimeOut, err := runtimeUC.Execute(app.Ctx(), usecase.CheckRuntimeDependenciesInput{
62- Prefix: app.Config.Runtime.Prefix,62+ Prefix: app.Config.Engine.WebKit.Prefix,
63 })63 })
64 if err != nil {64 if err != nil {
65 return err65 return err
M internal/infrastructure/config/defaults.go +3 -7
@@ -149,17 +149,13 @@ func DefaultConfig() *Config {
149149 ZoomCacheSize: defaultZoomCacheSize,
150150 CookiePolicy: CookiePolicyNoThirdParty,
151151 WebKit: WebKitEngineConfig{
152- ITPEnabled: true,
152+ ITPEnabled: true,
153153 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,
154154 SkiaGPUPaintingThreads: defaultSkiaGPUPaintingThreads,
155- GSKRenderer: GSKRendererAuto,
156- GLRenderingMode: GLRenderingModeAuto,
155+ GSKRenderer: GSKRendererAuto,
156+ GLRenderingMode: GLRenderingModeAuto,
157157 },
158158 },
159- // Rendering, Privacy, Runtime, Performance: zeroed out.
160- // These sections have been migrated to [engine]/[engine.webkit].
161- // Keeping zero-value structs so the Config type remains backward-compatible
162- // for unmarshalling old config files during migration.
163159 DefaultWebpageZoom: 1.2, // 120% default zoom for better readability
164160 DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200%
165161 Workspace: WorkspaceConfig{
@@ -149,17 +149,13 @@ func DefaultConfig() *Config {
149 ZoomCacheSize: defaultZoomCacheSize,149 ZoomCacheSize: defaultZoomCacheSize,
150 CookiePolicy: CookiePolicyNoThirdParty,150 CookiePolicy: CookiePolicyNoThirdParty,
151 WebKit: WebKitEngineConfig{151 WebKit: WebKitEngineConfig{
152- ITPEnabled: true,152+ ITPEnabled: true,
153 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,153 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,
154 SkiaGPUPaintingThreads: defaultSkiaGPUPaintingThreads,154 SkiaGPUPaintingThreads: defaultSkiaGPUPaintingThreads,
155- GSKRenderer: GSKRendererAuto,155+ GSKRenderer: GSKRendererAuto,
156- GLRenderingMode: GLRenderingModeAuto,156+ GLRenderingMode: GLRenderingModeAuto,
157 },157 },
158 },158 },
159- // Rendering, Privacy, Runtime, Performance: zeroed out.
160- // These sections have been migrated to [engine]/[engine.webkit].
161- // Keeping zero-value structs so the Config type remains backward-compatible
162- // for unmarshalling old config files during migration.
163 DefaultWebpageZoom: 1.2, // 120% default zoom for better readability159 DefaultWebpageZoom: 1.2, // 120% default zoom for better readability
164 DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200%160 DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200%
165 Workspace: WorkspaceConfig{161 Workspace: WorkspaceConfig{
M internal/infrastructure/config/defaults_test.go +2 -4
@@ -18,8 +18,6 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) {
1818 assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
1919 assert.True(t, cfg.Engine.WebKit.ITPEnabled)
2020
21- // Old sections should be zero-valued
22- assert.Empty(t, string(cfg.Performance.Profile))
23- assert.Empty(t, string(cfg.Privacy.CookiePolicy))
24- assert.Empty(t, string(cfg.Rendering.Mode))
21+ // Old sections (Rendering, Privacy, Performance, Runtime) have been removed from Config.
22+ // Their values now live under cfg.Engine / cfg.Engine.WebKit (validated above).
2523 }
@@ -18,8 +18,6 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) {
18 assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)18 assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
19 assert.True(t, cfg.Engine.WebKit.ITPEnabled)19 assert.True(t, cfg.Engine.WebKit.ITPEnabled)
20 20
21- // Old sections should be zero-valued21+ // Old sections (Rendering, Privacy, Performance, Runtime) have been removed from Config.
22- assert.Empty(t, string(cfg.Performance.Profile))22+ // Their values now live under cfg.Engine / cfg.Engine.WebKit (validated above).
23- assert.Empty(t, string(cfg.Privacy.CookiePolicy))
24- assert.Empty(t, string(cfg.Rendering.Mode))
25 }23 }
M internal/infrastructure/config/engine.go +24 -0
@@ -56,3 +56,27 @@ type WebKitEngineConfig struct {
5656 NetworkProcessMemoryConservativeThreshold float64 `mapstructure:"network_process_memory_conservative_threshold" toml:"network_process_memory_conservative_threshold" yaml:"network_process_memory_conservative_threshold"` //nolint:lll // struct tags exceed lll limit
5757 NetworkProcessMemoryStrictThreshold float64 `mapstructure:"network_process_memory_strict_threshold" toml:"network_process_memory_strict_threshold" yaml:"network_process_memory_strict_threshold"` //nolint:lll // struct tags exceed lll limit
5858 }
59+
60+// PerformanceConfigFromEngine constructs a PerformanceConfig from the
61+// new [engine] config section, for use with ResolvePerformanceProfile.
62+func PerformanceConfigFromEngine(e *EngineConfig) PerformanceConfig {
63+ if e == nil {
64+ return PerformanceConfig{}
65+ }
66+ return PerformanceConfig{
67+ Profile: e.Profile,
68+ ZoomCacheSize: e.ZoomCacheSize,
69+ WebViewPoolPrewarmCount: e.PoolPrewarmCount,
70+ SkiaCPUPaintingThreads: e.WebKit.SkiaCPUPaintingThreads,
71+ SkiaGPUPaintingThreads: e.WebKit.SkiaGPUPaintingThreads,
72+ SkiaEnableCPURendering: e.WebKit.SkiaEnableCPURendering,
73+ WebProcessMemoryLimitMB: e.WebKit.WebProcessMemoryLimitMB,
74+ WebProcessMemoryPollIntervalSec: e.WebKit.WebProcessMemoryPollIntervalSec,
75+ WebProcessMemoryConservativeThreshold: e.WebKit.WebProcessMemoryConservativeThreshold,
76+ WebProcessMemoryStrictThreshold: e.WebKit.WebProcessMemoryStrictThreshold,
77+ NetworkProcessMemoryLimitMB: e.WebKit.NetworkProcessMemoryLimitMB,
78+ NetworkProcessMemoryPollIntervalSec: e.WebKit.NetworkProcessMemoryPollIntervalSec,
79+ NetworkProcessMemoryConservativeThreshold: e.WebKit.NetworkProcessMemoryConservativeThreshold,
80+ NetworkProcessMemoryStrictThreshold: e.WebKit.NetworkProcessMemoryStrictThreshold,
81+ }
82+}
@@ -56,3 +56,27 @@ type WebKitEngineConfig struct {
56 NetworkProcessMemoryConservativeThreshold float64 `mapstructure:"network_process_memory_conservative_threshold" toml:"network_process_memory_conservative_threshold" yaml:"network_process_memory_conservative_threshold"` //nolint:lll // struct tags exceed lll limit56 NetworkProcessMemoryConservativeThreshold float64 `mapstructure:"network_process_memory_conservative_threshold" toml:"network_process_memory_conservative_threshold" yaml:"network_process_memory_conservative_threshold"` //nolint:lll // struct tags exceed lll limit
57 NetworkProcessMemoryStrictThreshold float64 `mapstructure:"network_process_memory_strict_threshold" toml:"network_process_memory_strict_threshold" yaml:"network_process_memory_strict_threshold"` //nolint:lll // struct tags exceed lll limit57 NetworkProcessMemoryStrictThreshold float64 `mapstructure:"network_process_memory_strict_threshold" toml:"network_process_memory_strict_threshold" yaml:"network_process_memory_strict_threshold"` //nolint:lll // struct tags exceed lll limit
58 }58 }
59+
60+// PerformanceConfigFromEngine constructs a PerformanceConfig from the
61+// new [engine] config section, for use with ResolvePerformanceProfile.
62+func PerformanceConfigFromEngine(e *EngineConfig) PerformanceConfig {
63+ if e == nil {
64+ return PerformanceConfig{}
65+ }
66+ return PerformanceConfig{
67+ Profile: e.Profile,
68+ ZoomCacheSize: e.ZoomCacheSize,
69+ WebViewPoolPrewarmCount: e.PoolPrewarmCount,
70+ SkiaCPUPaintingThreads: e.WebKit.SkiaCPUPaintingThreads,
71+ SkiaGPUPaintingThreads: e.WebKit.SkiaGPUPaintingThreads,
72+ SkiaEnableCPURendering: e.WebKit.SkiaEnableCPURendering,
73+ WebProcessMemoryLimitMB: e.WebKit.WebProcessMemoryLimitMB,
74+ WebProcessMemoryPollIntervalSec: e.WebKit.WebProcessMemoryPollIntervalSec,
75+ WebProcessMemoryConservativeThreshold: e.WebKit.WebProcessMemoryConservativeThreshold,
76+ WebProcessMemoryStrictThreshold: e.WebKit.WebProcessMemoryStrictThreshold,
77+ NetworkProcessMemoryLimitMB: e.WebKit.NetworkProcessMemoryLimitMB,
78+ NetworkProcessMemoryPollIntervalSec: e.WebKit.NetworkProcessMemoryPollIntervalSec,
79+ NetworkProcessMemoryConservativeThreshold: e.WebKit.NetworkProcessMemoryConservativeThreshold,
80+ NetworkProcessMemoryStrictThreshold: e.WebKit.NetworkProcessMemoryStrictThreshold,
81+ }
82+}
M internal/infrastructure/config/loader.go +32 -45
@@ -204,52 +204,54 @@ func ensureDatabasePath(config *Config) error {
204204 }
205205
206206 func normalizeConfig(config *Config) {
207- normalizeRendering(config)
208- normalizePrivacy(config)
209207 normalizeAppearance(config)
210208 normalizeMedia(config)
211-
212- config.Runtime.Prefix = strings.TrimSpace(config.Runtime.Prefix)
213- normalizePerformanceProfile(config)
209+ normalizeEngineConfig(config)
214210 }
215211
216-func normalizeRendering(config *Config) {
217- switch strings.ToLower(string(config.Rendering.Mode)) {
218- case "", string(RenderingModeAuto):
219- config.Rendering.Mode = RenderingModeAuto
220- case string(RenderingModeGPU):
221- config.Rendering.Mode = RenderingModeGPU
222- case string(RenderingModeCPU):
223- config.Rendering.Mode = RenderingModeCPU
224- default:
225- config.Rendering.Mode = RenderingModeAuto
226- }
227-
228- switch strings.ToLower(string(config.Rendering.GSKRenderer)) {
212+func normalizeEngineConfig(config *Config) {
213+ // Normalize GSK renderer (engine.webkit)
214+ switch strings.ToLower(string(config.Engine.WebKit.GSKRenderer)) {
229215 case "", string(GSKRendererAuto):
230- config.Rendering.GSKRenderer = GSKRendererAuto
216+ config.Engine.WebKit.GSKRenderer = GSKRendererAuto
231217 case string(GSKRendererOpenGL):
232- config.Rendering.GSKRenderer = GSKRendererOpenGL
218+ config.Engine.WebKit.GSKRenderer = GSKRendererOpenGL
233219 case string(GSKRendererVulkan):
234- config.Rendering.GSKRenderer = GSKRendererVulkan
220+ config.Engine.WebKit.GSKRenderer = GSKRendererVulkan
235221 case string(GSKRendererCairo):
236- config.Rendering.GSKRenderer = GSKRendererCairo
222+ config.Engine.WebKit.GSKRenderer = GSKRendererCairo
237223 default:
238- config.Rendering.GSKRenderer = GSKRendererAuto
224+ config.Engine.WebKit.GSKRenderer = GSKRendererAuto
239225 }
240-}
241226
242-func normalizePrivacy(config *Config) {
243- switch strings.ToLower(string(config.Privacy.CookiePolicy)) {
227+ // Normalize cookie policy (engine)
228+ switch strings.ToLower(string(config.Engine.CookiePolicy)) {
244229 case "", string(CookiePolicyNoThirdParty):
245- config.Privacy.CookiePolicy = CookiePolicyNoThirdParty
230+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
246231 case string(CookiePolicyAlways):
247- config.Privacy.CookiePolicy = CookiePolicyAlways
232+ config.Engine.CookiePolicy = CookiePolicyAlways
248233 case string(CookiePolicyNever):
249- config.Privacy.CookiePolicy = CookiePolicyNever
234+ config.Engine.CookiePolicy = CookiePolicyNever
250235 default:
251- config.Privacy.CookiePolicy = CookiePolicyNoThirdParty
236+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
252237 }
238+
239+ // Normalize performance profile (engine)
240+ switch strings.ToLower(string(config.Engine.Profile)) {
241+ case "", string(ProfileDefault):
242+ config.Engine.Profile = ProfileDefault
243+ case string(ProfileLite):
244+ config.Engine.Profile = ProfileLite
245+ case string(ProfileMax):
246+ config.Engine.Profile = ProfileMax
247+ case string(ProfileCustom):
248+ config.Engine.Profile = ProfileCustom
249+ default:
250+ config.Engine.Profile = ProfileDefault
251+ }
252+
253+ // Trim runtime prefix (engine.webkit)
254+ config.Engine.WebKit.Prefix = strings.TrimSpace(config.Engine.WebKit.Prefix)
253255 }
254256
255257 func normalizeAppearance(config *Config) {
@@ -291,21 +293,6 @@ func normalizeMedia(config *Config) {
291293 }
292294 }
293295
294-func normalizePerformanceProfile(config *Config) {
295- switch strings.ToLower(string(config.Performance.Profile)) {
296- case "", string(ProfileDefault):
297- config.Performance.Profile = ProfileDefault
298- case string(ProfileLite):
299- config.Performance.Profile = ProfileLite
300- case string(ProfileMax):
301- config.Performance.Profile = ProfileMax
302- case string(ProfileCustom):
303- config.Performance.Profile = ProfileCustom
304- default:
305- config.Performance.Profile = ProfileDefault
306- }
307-}
308-
309296 // Get returns the current configuration (thread-safe).
310297 func (m *Manager) Get() *Config {
311298 m.mu.RLock()
@@ -204,52 +204,54 @@ func ensureDatabasePath(config *Config) error {
204 }204 }
205 205
206 func normalizeConfig(config *Config) {206 func normalizeConfig(config *Config) {
207- normalizeRendering(config)
208- normalizePrivacy(config)
209 normalizeAppearance(config)207 normalizeAppearance(config)
210 normalizeMedia(config)208 normalizeMedia(config)
211-209+ normalizeEngineConfig(config)
212- config.Runtime.Prefix = strings.TrimSpace(config.Runtime.Prefix)
213- normalizePerformanceProfile(config)
214 }210 }
215 211
216-func normalizeRendering(config *Config) {212+func normalizeEngineConfig(config *Config) {
217- switch strings.ToLower(string(config.Rendering.Mode)) {213+ // Normalize GSK renderer (engine.webkit)
218- case "", string(RenderingModeAuto):214+ switch strings.ToLower(string(config.Engine.WebKit.GSKRenderer)) {
219- config.Rendering.Mode = RenderingModeAuto
220- case string(RenderingModeGPU):
221- config.Rendering.Mode = RenderingModeGPU
222- case string(RenderingModeCPU):
223- config.Rendering.Mode = RenderingModeCPU
224- default:
225- config.Rendering.Mode = RenderingModeAuto
226- }
227-
228- switch strings.ToLower(string(config.Rendering.GSKRenderer)) {
229 case "", string(GSKRendererAuto):215 case "", string(GSKRendererAuto):
230- config.Rendering.GSKRenderer = GSKRendererAuto216+ config.Engine.WebKit.GSKRenderer = GSKRendererAuto
231 case string(GSKRendererOpenGL):217 case string(GSKRendererOpenGL):
232- config.Rendering.GSKRenderer = GSKRendererOpenGL218+ config.Engine.WebKit.GSKRenderer = GSKRendererOpenGL
233 case string(GSKRendererVulkan):219 case string(GSKRendererVulkan):
234- config.Rendering.GSKRenderer = GSKRendererVulkan220+ config.Engine.WebKit.GSKRenderer = GSKRendererVulkan
235 case string(GSKRendererCairo):221 case string(GSKRendererCairo):
236- config.Rendering.GSKRenderer = GSKRendererCairo222+ config.Engine.WebKit.GSKRenderer = GSKRendererCairo
237 default:223 default:
238- config.Rendering.GSKRenderer = GSKRendererAuto224+ config.Engine.WebKit.GSKRenderer = GSKRendererAuto
239 }225 }
240-}
241 226
242-func normalizePrivacy(config *Config) {227+ // Normalize cookie policy (engine)
243- switch strings.ToLower(string(config.Privacy.CookiePolicy)) {228+ switch strings.ToLower(string(config.Engine.CookiePolicy)) {
244 case "", string(CookiePolicyNoThirdParty):229 case "", string(CookiePolicyNoThirdParty):
245- config.Privacy.CookiePolicy = CookiePolicyNoThirdParty230+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
246 case string(CookiePolicyAlways):231 case string(CookiePolicyAlways):
247- config.Privacy.CookiePolicy = CookiePolicyAlways232+ config.Engine.CookiePolicy = CookiePolicyAlways
248 case string(CookiePolicyNever):233 case string(CookiePolicyNever):
249- config.Privacy.CookiePolicy = CookiePolicyNever234+ config.Engine.CookiePolicy = CookiePolicyNever
250 default:235 default:
251- config.Privacy.CookiePolicy = CookiePolicyNoThirdParty236+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
252 }237 }
238+
239+ // Normalize performance profile (engine)
240+ switch strings.ToLower(string(config.Engine.Profile)) {
241+ case "", string(ProfileDefault):
242+ config.Engine.Profile = ProfileDefault
243+ case string(ProfileLite):
244+ config.Engine.Profile = ProfileLite
245+ case string(ProfileMax):
246+ config.Engine.Profile = ProfileMax
247+ case string(ProfileCustom):
248+ config.Engine.Profile = ProfileCustom
249+ default:
250+ config.Engine.Profile = ProfileDefault
251+ }
252+
253+ // Trim runtime prefix (engine.webkit)
254+ config.Engine.WebKit.Prefix = strings.TrimSpace(config.Engine.WebKit.Prefix)
253 }255 }
254 256
255 func normalizeAppearance(config *Config) {257 func normalizeAppearance(config *Config) {
@@ -291,21 +293,6 @@ func normalizeMedia(config *Config) {
291 }293 }
292 }294 }
293 295
294-func normalizePerformanceProfile(config *Config) {
295- switch strings.ToLower(string(config.Performance.Profile)) {
296- case "", string(ProfileDefault):
297- config.Performance.Profile = ProfileDefault
298- case string(ProfileLite):
299- config.Performance.Profile = ProfileLite
300- case string(ProfileMax):
301- config.Performance.Profile = ProfileMax
302- case string(ProfileCustom):
303- config.Performance.Profile = ProfileCustom
304- default:
305- config.Performance.Profile = ProfileDefault
306- }
307-}
308-
309 // Get returns the current configuration (thread-safe).296 // Get returns the current configuration (thread-safe).
310 func (m *Manager) Get() *Config {297 func (m *Manager) Get() *Config {
311 m.mu.RLock()298 m.mu.RLock()
M internal/infrastructure/config/loader_test.go +3 -3
@@ -31,11 +31,11 @@ func TestSetEngineDefaults(t *testing.T) {
3131 assert.Empty(t, mgr.viper.GetString("runtime.prefix"))
3232 }
3333
34-func TestNormalizeConfig_PrivacyCookiePolicy(t *testing.T) {
34+func TestNormalizeConfig_EngineCookiePolicy(t *testing.T) {
3535 cfg := DefaultConfig()
36- cfg.Privacy.CookiePolicy = CookiePolicy("INVALID")
36+ cfg.Engine.CookiePolicy = CookiePolicy("INVALID")
3737
3838 normalizeConfig(cfg)
3939
40- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Privacy.CookiePolicy)
40+ assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
4141 }
@@ -31,11 +31,11 @@ func TestSetEngineDefaults(t *testing.T) {
31 assert.Empty(t, mgr.viper.GetString("runtime.prefix"))31 assert.Empty(t, mgr.viper.GetString("runtime.prefix"))
32 }32 }
33 33
34-func TestNormalizeConfig_PrivacyCookiePolicy(t *testing.T) {34+func TestNormalizeConfig_EngineCookiePolicy(t *testing.T) {
35 cfg := DefaultConfig()35 cfg := DefaultConfig()
36- cfg.Privacy.CookiePolicy = CookiePolicy("INVALID")36+ cfg.Engine.CookiePolicy = CookiePolicy("INVALID")
37 37
38 normalizeConfig(cfg)38 normalizeConfig(cfg)
39 39
40- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Privacy.CookiePolicy)40+ assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
41 }41 }
M internal/infrastructure/config/schema.go +0 -78
@@ -25,16 +25,8 @@ type Config struct {
2525 Clipboard ClipboardConfig `mapstructure:"clipboard" yaml:"clipboard" toml:"clipboard"`
2626 // Omnibox controls the omnibox behavior (initial history display)
2727 Omnibox OmniboxConfig `mapstructure:"omnibox" yaml:"omnibox" toml:"omnibox"`
28- // Rendering controls WebKit, GTK and compositor behavior.
29- Rendering RenderingConfig `mapstructure:"rendering" yaml:"rendering" toml:"rendering"`
30- // Privacy controls cookie acceptance and tracking prevention.
31- Privacy PrivacyConfig `mapstructure:"privacy" yaml:"privacy" toml:"privacy"`
3228 // Media controls video playback and hardware acceleration
3329 Media MediaConfig `mapstructure:"media" yaml:"media" toml:"media"`
34- // Runtime configures optional runtime overrides (e.g., /opt prefix for WebKitGTK/GTK).
35- Runtime RuntimeConfig `mapstructure:"runtime" yaml:"runtime" toml:"runtime"`
36- // Performance holds internal performance tuning options (not exposed in UI).
37- Performance PerformanceConfig `mapstructure:"performance" yaml:"performance" toml:"performance"`
3830 // Update controls automatic update checking and downloading.
3931 Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"`
4032 // Downloads configures file download behavior.
@@ -43,15 +35,6 @@ type Config struct {
4335 Engine EngineConfig `mapstructure:"engine" toml:"engine" yaml:"engine"`
4436 }
4537
46-// RenderingMode selects GPU vs CPU rendering.
47-type RenderingMode string
48-
49-const (
50- RenderingModeAuto RenderingMode = "auto"
51- RenderingModeGPU RenderingMode = "gpu"
52- RenderingModeCPU RenderingMode = "cpu"
53-)
54-
5538 // CookiePolicy controls cookie acceptance behavior.
5639 type CookiePolicy string
5740
@@ -74,60 +57,6 @@ const (
7457 GSKRendererCairo GSKRendererMode = "cairo"
7558 )
7659
77-// RenderingConfig controls WebKit and GTK/GSK rendering behavior.
78-type RenderingConfig struct {
79- // Mode controls GPU/CPU rendering selection for WebKit (auto, gpu, cpu).
80- Mode RenderingMode `mapstructure:"mode" yaml:"mode" toml:"mode"`
81-
82- // DisableDMABufRenderer disables DMA-BUF accelerated renderer.
83- // Env: WEBKIT_DISABLE_DMABUF_RENDERER
84- DisableDMABufRenderer bool `mapstructure:"disable_dmabuf_renderer" yaml:"disable_dmabuf_renderer" toml:"disable_dmabuf_renderer"`
85-
86- // ForceCompositingMode forces accelerated compositing.
87- // Env: WEBKIT_FORCE_COMPOSITING_MODE
88- ForceCompositingMode bool `mapstructure:"force_compositing_mode" yaml:"force_compositing_mode" toml:"force_compositing_mode"`
89-
90- // DisableCompositingMode disables accelerated compositing.
91- // Env: WEBKIT_DISABLE_COMPOSITING_MODE
92- DisableCompositingMode bool `mapstructure:"disable_compositing_mode" yaml:"disable_compositing_mode" toml:"disable_compositing_mode"`
93-
94- // GSKRenderer overrides GTK Scene Kit renderer selection.
95- // Env: GSK_RENDERER
96- GSKRenderer GSKRendererMode `mapstructure:"gsk_renderer" yaml:"gsk_renderer" toml:"gsk_renderer"`
97-
98- // DisableMipmaps disables mipmap generation.
99- // Env: GSK_GPU_DISABLE=mipmap
100- DisableMipmaps bool `mapstructure:"disable_mipmaps" yaml:"disable_mipmaps" toml:"disable_mipmaps"`
101-
102- // PreferGL forces OpenGL over GLES.
103- // Env: GDK_DEBUG=gl-prefer-gl
104- PreferGL bool `mapstructure:"prefer_gl" yaml:"prefer_gl" toml:"prefer_gl"`
105-
106- // DrawCompositingIndicators shows compositing layer borders and repaint counters.
107- DrawCompositingIndicators bool `mapstructure:"draw_compositing_indicators" yaml:"draw_compositing_indicators" toml:"draw_compositing_indicators"` //nolint:lll // struct tags exceed lll limit
108-
109- // ShowFPS displays WebKit FPS counter.
110- // Env: WEBKIT_SHOW_FPS
111- ShowFPS bool `mapstructure:"show_fps" yaml:"show_fps" toml:"show_fps"`
112-
113- // SampleMemory enables memory sampling.
114- // Env: WEBKIT_SAMPLE_MEMORY
115- SampleMemory bool `mapstructure:"sample_memory" yaml:"sample_memory" toml:"sample_memory"`
116-
117- // DebugFrames enables frame timing debug output.
118- // Env: GDK_DEBUG=frames
119- DebugFrames bool `mapstructure:"debug_frames" yaml:"debug_frames" toml:"debug_frames"`
120-}
121-
122-// PrivacyConfig controls cookie behavior and tracking prevention.
123-type PrivacyConfig struct {
124- // CookiePolicy controls cookie acceptance behavior.
125- // Values: "always", "no_third_party", "never"
126- CookiePolicy CookiePolicy `mapstructure:"cookie_policy" yaml:"cookie_policy" toml:"cookie_policy"`
127- // ITPEnabled enables Intelligent Tracking Prevention.
128- ITPEnabled bool `mapstructure:"itp_enabled" yaml:"itp_enabled" toml:"itp_enabled"`
129-}
130-
13160 // HardwareDecodingMode controls video hardware acceleration.
13261 type HardwareDecodingMode string
13362
@@ -196,13 +125,6 @@ type MediaConfig struct {
196125 GStreamerDebugLevel int `mapstructure:"gstreamer_debug_level" yaml:"gstreamer_debug_level" toml:"gstreamer_debug_level"`
197126 }
198127
199-// RuntimeConfig holds optional runtime overrides.
200-type RuntimeConfig struct {
201- // Prefix points to a custom runtime prefix (e.g., /opt/webkitgtk).
202- // This is used to influence pkg-config lookup and dynamic library loading.
203- Prefix string `mapstructure:"prefix" yaml:"prefix" toml:"prefix"`
204-}
205-
206128 // DatabaseConfig holds database-related configuration.
207129 type DatabaseConfig struct {
208130 Path string `mapstructure:"path" yaml:"path" toml:"path"`
@@ -25,16 +25,8 @@ type Config struct {
25 Clipboard ClipboardConfig `mapstructure:"clipboard" yaml:"clipboard" toml:"clipboard"`25 Clipboard ClipboardConfig `mapstructure:"clipboard" yaml:"clipboard" toml:"clipboard"`
26 // Omnibox controls the omnibox behavior (initial history display)26 // Omnibox controls the omnibox behavior (initial history display)
27 Omnibox OmniboxConfig `mapstructure:"omnibox" yaml:"omnibox" toml:"omnibox"`27 Omnibox OmniboxConfig `mapstructure:"omnibox" yaml:"omnibox" toml:"omnibox"`
28- // Rendering controls WebKit, GTK and compositor behavior.
29- Rendering RenderingConfig `mapstructure:"rendering" yaml:"rendering" toml:"rendering"`
30- // Privacy controls cookie acceptance and tracking prevention.
31- Privacy PrivacyConfig `mapstructure:"privacy" yaml:"privacy" toml:"privacy"`
32 // Media controls video playback and hardware acceleration28 // Media controls video playback and hardware acceleration
33 Media MediaConfig `mapstructure:"media" yaml:"media" toml:"media"`29 Media MediaConfig `mapstructure:"media" yaml:"media" toml:"media"`
34- // Runtime configures optional runtime overrides (e.g., /opt prefix for WebKitGTK/GTK).
35- Runtime RuntimeConfig `mapstructure:"runtime" yaml:"runtime" toml:"runtime"`
36- // Performance holds internal performance tuning options (not exposed in UI).
37- Performance PerformanceConfig `mapstructure:"performance" yaml:"performance" toml:"performance"`
38 // Update controls automatic update checking and downloading.30 // Update controls automatic update checking and downloading.
39 Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"`31 Update UpdateConfig `mapstructure:"update" yaml:"update" toml:"update"`
40 // Downloads configures file download behavior.32 // Downloads configures file download behavior.
@@ -43,15 +35,6 @@ type Config struct {
43 Engine EngineConfig `mapstructure:"engine" toml:"engine" yaml:"engine"`35 Engine EngineConfig `mapstructure:"engine" toml:"engine" yaml:"engine"`
44 }36 }
45 37
46-// RenderingMode selects GPU vs CPU rendering.
47-type RenderingMode string
48-
49-const (
50- RenderingModeAuto RenderingMode = "auto"
51- RenderingModeGPU RenderingMode = "gpu"
52- RenderingModeCPU RenderingMode = "cpu"
53-)
54-
55 // CookiePolicy controls cookie acceptance behavior.38 // CookiePolicy controls cookie acceptance behavior.
56 type CookiePolicy string39 type CookiePolicy string
57 40
@@ -74,60 +57,6 @@ const (
74 GSKRendererCairo GSKRendererMode = "cairo"57 GSKRendererCairo GSKRendererMode = "cairo"
75 )58 )
76 59
77-// RenderingConfig controls WebKit and GTK/GSK rendering behavior.
78-type RenderingConfig struct {
79- // Mode controls GPU/CPU rendering selection for WebKit (auto, gpu, cpu).
80- Mode RenderingMode `mapstructure:"mode" yaml:"mode" toml:"mode"`
81-
82- // DisableDMABufRenderer disables DMA-BUF accelerated renderer.
83- // Env: WEBKIT_DISABLE_DMABUF_RENDERER
84- DisableDMABufRenderer bool `mapstructure:"disable_dmabuf_renderer" yaml:"disable_dmabuf_renderer" toml:"disable_dmabuf_renderer"`
85-
86- // ForceCompositingMode forces accelerated compositing.
87- // Env: WEBKIT_FORCE_COMPOSITING_MODE
88- ForceCompositingMode bool `mapstructure:"force_compositing_mode" yaml:"force_compositing_mode" toml:"force_compositing_mode"`
89-
90- // DisableCompositingMode disables accelerated compositing.
91- // Env: WEBKIT_DISABLE_COMPOSITING_MODE
92- DisableCompositingMode bool `mapstructure:"disable_compositing_mode" yaml:"disable_compositing_mode" toml:"disable_compositing_mode"`
93-
94- // GSKRenderer overrides GTK Scene Kit renderer selection.
95- // Env: GSK_RENDERER
96- GSKRenderer GSKRendererMode `mapstructure:"gsk_renderer" yaml:"gsk_renderer" toml:"gsk_renderer"`
97-
98- // DisableMipmaps disables mipmap generation.
99- // Env: GSK_GPU_DISABLE=mipmap
100- DisableMipmaps bool `mapstructure:"disable_mipmaps" yaml:"disable_mipmaps" toml:"disable_mipmaps"`
101-
102- // PreferGL forces OpenGL over GLES.
103- // Env: GDK_DEBUG=gl-prefer-gl
104- PreferGL bool `mapstructure:"prefer_gl" yaml:"prefer_gl" toml:"prefer_gl"`
105-
106- // DrawCompositingIndicators shows compositing layer borders and repaint counters.
107- DrawCompositingIndicators bool `mapstructure:"draw_compositing_indicators" yaml:"draw_compositing_indicators" toml:"draw_compositing_indicators"` //nolint:lll // struct tags exceed lll limit
108-
109- // ShowFPS displays WebKit FPS counter.
110- // Env: WEBKIT_SHOW_FPS
111- ShowFPS bool `mapstructure:"show_fps" yaml:"show_fps" toml:"show_fps"`
112-
113- // SampleMemory enables memory sampling.
114- // Env: WEBKIT_SAMPLE_MEMORY
115- SampleMemory bool `mapstructure:"sample_memory" yaml:"sample_memory" toml:"sample_memory"`
116-
117- // DebugFrames enables frame timing debug output.
118- // Env: GDK_DEBUG=frames
119- DebugFrames bool `mapstructure:"debug_frames" yaml:"debug_frames" toml:"debug_frames"`
120-}
121-
122-// PrivacyConfig controls cookie behavior and tracking prevention.
123-type PrivacyConfig struct {
124- // CookiePolicy controls cookie acceptance behavior.
125- // Values: "always", "no_third_party", "never"
126- CookiePolicy CookiePolicy `mapstructure:"cookie_policy" yaml:"cookie_policy" toml:"cookie_policy"`
127- // ITPEnabled enables Intelligent Tracking Prevention.
128- ITPEnabled bool `mapstructure:"itp_enabled" yaml:"itp_enabled" toml:"itp_enabled"`
129-}
130-
131 // HardwareDecodingMode controls video hardware acceleration.60 // HardwareDecodingMode controls video hardware acceleration.
132 type HardwareDecodingMode string61 type HardwareDecodingMode string
133 62
@@ -196,13 +125,6 @@ type MediaConfig struct {
196 GStreamerDebugLevel int `mapstructure:"gstreamer_debug_level" yaml:"gstreamer_debug_level" toml:"gstreamer_debug_level"`125 GStreamerDebugLevel int `mapstructure:"gstreamer_debug_level" yaml:"gstreamer_debug_level" toml:"gstreamer_debug_level"`
197 }126 }
198 127
199-// RuntimeConfig holds optional runtime overrides.
200-type RuntimeConfig struct {
201- // Prefix points to a custom runtime prefix (e.g., /opt/webkitgtk).
202- // This is used to influence pkg-config lookup and dynamic library loading.
203- Prefix string `mapstructure:"prefix" yaml:"prefix" toml:"prefix"`
204-}
205-
206 // DatabaseConfig holds database-related configuration.128 // DatabaseConfig holds database-related configuration.
207 type DatabaseConfig struct {129 type DatabaseConfig struct {
208 Path string `mapstructure:"path" yaml:"path" toml:"path"`130 Path string `mapstructure:"path" yaml:"path" toml:"path"`
M internal/infrastructure/config/schema_provider.go +51 -59
@@ -712,81 +712,73 @@ func (*SchemaProvider) getClipboardKeys(defaults *Config) []entity.ConfigKeyInfo
712712 func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {
713713 return []entity.ConfigKeyInfo{
714714 {
715- Key: "rendering.mode",
715+ Key: "engine.webkit.gsk_renderer",
716716 Type: "string",
717- Default: string(defaults.Rendering.Mode),
718- Description: "GPU/CPU rendering selection for WebKit",
719- Values: []string{"auto", "gpu", "cpu"},
720- Section: SectionRendering,
721- },
722- {
723- Key: "rendering.gsk_renderer",
724- Type: "string",
725- Default: string(defaults.Rendering.GSKRenderer),
717+ Default: string(defaults.Engine.WebKit.GSKRenderer),
726718 Description: "GTK Scene Kit renderer selection",
727719 Values: []string{"auto", "opengl", "vulkan", "cairo"},
728720 Section: SectionRendering,
729721 },
730722 {
731- Key: "rendering.disable_dmabuf_renderer",
723+ Key: "engine.webkit.disable_dmabuf_renderer",
732724 Type: "bool",
733- Default: fmt.Sprintf("%t", defaults.Rendering.DisableDMABufRenderer),
725+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableDMABufRenderer),
734726 Description: "Disable DMA-BUF accelerated renderer",
735727 Section: SectionRendering,
736728 },
737729 {
738- Key: "rendering.force_compositing_mode",
730+ Key: "engine.webkit.force_compositing_mode",
739731 Type: "bool",
740- Default: fmt.Sprintf("%t", defaults.Rendering.ForceCompositingMode),
732+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ForceCompositingMode),
741733 Description: "Force accelerated compositing",
742734 Section: SectionRendering,
743735 },
744736 {
745- Key: "rendering.disable_compositing_mode",
737+ Key: "engine.webkit.disable_compositing_mode",
746738 Type: "bool",
747- Default: fmt.Sprintf("%t", defaults.Rendering.DisableCompositingMode),
739+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableCompositingMode),
748740 Description: "Disable accelerated compositing",
749741 Section: SectionRendering,
750742 },
751743 {
752- Key: "rendering.disable_mipmaps",
744+ Key: "engine.webkit.disable_mipmaps",
753745 Type: "bool",
754- Default: fmt.Sprintf("%t", defaults.Rendering.DisableMipmaps),
746+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableMipmaps),
755747 Description: "Disable mipmap generation",
756748 Section: SectionRendering,
757749 },
758750 {
759- Key: "rendering.prefer_gl",
751+ Key: "engine.webkit.prefer_gl",
760752 Type: "bool",
761- Default: fmt.Sprintf("%t", defaults.Rendering.PreferGL),
753+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.PreferGL),
762754 Description: "Force OpenGL over GLES",
763755 Section: SectionRendering,
764756 },
765757 {
766- Key: "rendering.draw_compositing_indicators",
758+ Key: "engine.webkit.draw_compositing_indicators",
767759 Type: "bool",
768- Default: fmt.Sprintf("%t", defaults.Rendering.DrawCompositingIndicators),
760+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DrawCompositingIndicators),
769761 Description: "Show compositing layer borders",
770762 Section: SectionRendering,
771763 },
772764 {
773- Key: "rendering.show_fps",
765+ Key: "engine.webkit.show_fps",
774766 Type: "bool",
775- Default: fmt.Sprintf("%t", defaults.Rendering.ShowFPS),
767+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ShowFPS),
776768 Description: "Display WebKit FPS counter",
777769 Section: SectionRendering,
778770 },
779771 {
780- Key: "rendering.sample_memory",
772+ Key: "engine.webkit.sample_memory",
781773 Type: "bool",
782- Default: fmt.Sprintf("%t", defaults.Rendering.SampleMemory),
774+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SampleMemory),
783775 Description: "Enable memory sampling",
784776 Section: SectionRendering,
785777 },
786778 {
787- Key: "rendering.debug_frames",
779+ Key: "engine.webkit.debug_frames",
788780 Type: "bool",
789- Default: fmt.Sprintf("%t", defaults.Rendering.DebugFrames),
781+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DebugFrames),
790782 Description: "Enable frame timing debug output",
791783 Section: SectionRendering,
792784 },
@@ -884,112 +876,112 @@ func (*SchemaProvider) getDebugKeys(defaults *Config) []entity.ConfigKeyInfo {
884876 func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {
885877 return []entity.ConfigKeyInfo{
886878 {
887- Key: "performance.profile",
879+ Key: "engine.profile",
888880 Type: "string",
889- Default: string(defaults.Performance.Profile),
881+ Default: string(defaults.Engine.Profile),
890882 Description: "Performance profile preset (custom enables manual tuning)",
891883 Values: []string{"default", "lite", "max", "custom"},
892884 Section: SectionPerformance,
893885 },
894886 {
895- Key: "performance.zoom_cache_size",
887+ Key: "engine.zoom_cache_size",
896888 Type: "int",
897- Default: fmt.Sprintf("%d", defaults.Performance.ZoomCacheSize),
889+ Default: fmt.Sprintf("%d", defaults.Engine.ZoomCacheSize),
898890 Description: "Number of domain zoom levels to cache",
899891 Range: ">=0",
900892 Section: SectionPerformance,
901893 },
902894 {
903- Key: "performance.webview_pool_prewarm_count",
895+ Key: "engine.pool_prewarm_count",
904896 Type: "int",
905- Default: fmt.Sprintf("%d", defaults.Performance.WebViewPoolPrewarmCount),
897+ Default: fmt.Sprintf("%d", defaults.Engine.PoolPrewarmCount),
906898 Description: "WebViews to pre-create at startup",
907899 Range: ">=0",
908900 Section: SectionPerformance,
909901 },
910902 {
911- Key: "performance.skia_cpu_painting_threads",
903+ Key: "engine.webkit.skia_cpu_painting_threads",
912904 Type: "int",
913- Default: fmt.Sprintf("%d", defaults.Performance.SkiaCPUPaintingThreads),
905+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaCPUPaintingThreads),
914906 Description: "Skia CPU rendering threads (0=unset, custom profile only)",
915907 Range: ">=0",
916908 Section: SectionPerformance,
917909 },
918910 {
919- Key: "performance.skia_gpu_painting_threads",
911+ Key: "engine.webkit.skia_gpu_painting_threads",
920912 Type: "int",
921- Default: fmt.Sprintf("%d", defaults.Performance.SkiaGPUPaintingThreads),
913+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaGPUPaintingThreads),
922914 Description: "Skia GPU rendering threads (-1=unset, 0=disable, custom profile only)",
923915 Range: ">=-1",
924916 Section: SectionPerformance,
925917 },
926918 {
927- Key: "performance.skia_enable_cpu_rendering",
919+ Key: "engine.webkit.skia_enable_cpu_rendering",
928920 Type: "bool",
929- Default: fmt.Sprintf("%t", defaults.Performance.SkiaEnableCPURendering),
921+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SkiaEnableCPURendering),
930922 Description: "Force CPU rendering (custom profile only)",
931923 Section: SectionPerformance,
932924 },
933925 {
934- Key: "performance.web_process_memory_limit_mb",
926+ Key: "engine.webkit.web_process_memory_limit_mb",
935927 Type: "int",
936- Default: fmt.Sprintf("%d", defaults.Performance.WebProcessMemoryLimitMB),
928+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.WebProcessMemoryLimitMB),
937929 Description: "Web process memory limit in MB (0=unset, custom profile only)",
938930 Range: ">=0",
939931 Section: SectionPerformance,
940932 },
941933 {
942- Key: "performance.web_process_memory_poll_interval_sec",
934+ Key: "engine.webkit.web_process_memory_poll_interval_sec",
943935 Type: "float64",
944- Default: fmt.Sprintf("%.1f", defaults.Performance.WebProcessMemoryPollIntervalSec),
936+ Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.WebProcessMemoryPollIntervalSec),
945937 Description: "Memory check interval in seconds (0=default 30s, custom profile only)",
946938 Range: ">=0",
947939 Section: SectionPerformance,
948940 },
949941 {
950- Key: "performance.web_process_memory_conservative_threshold",
942+ Key: "engine.webkit.web_process_memory_conservative_threshold",
951943 Type: "float64",
952- Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryConservativeThreshold),
944+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryConservativeThreshold),
953945 Description: "Conservative memory cleanup threshold (0-1, custom profile only)",
954946 Range: "0-1",
955947 Section: SectionPerformance,
956948 },
957949 {
958- Key: "performance.web_process_memory_strict_threshold",
950+ Key: "engine.webkit.web_process_memory_strict_threshold",
959951 Type: "float64",
960- Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryStrictThreshold),
952+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryStrictThreshold),
961953 Description: "Strict memory cleanup threshold (0-1, custom profile only)",
962954 Range: "0-1",
963955 Section: SectionPerformance,
964956 },
965957 {
966- Key: "performance.network_process_memory_limit_mb",
958+ Key: "engine.webkit.network_process_memory_limit_mb",
967959 Type: "int",
968- Default: fmt.Sprintf("%d", defaults.Performance.NetworkProcessMemoryLimitMB),
960+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.NetworkProcessMemoryLimitMB),
969961 Description: "Network process memory limit in MB (0=unset, custom profile only)",
970962 Range: ">=0",
971963 Section: SectionPerformance,
972964 },
973965 {
974- Key: "performance.network_process_memory_poll_interval_sec",
966+ Key: "engine.webkit.network_process_memory_poll_interval_sec",
975967 Type: "float64",
976- Default: fmt.Sprintf("%.1f", defaults.Performance.NetworkProcessMemoryPollIntervalSec),
968+ Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.NetworkProcessMemoryPollIntervalSec),
977969 Description: "Network memory check interval in seconds (custom profile only)",
978970 Range: ">=0",
979971 Section: SectionPerformance,
980972 },
981973 {
982- Key: "performance.network_process_memory_conservative_threshold",
974+ Key: "engine.webkit.network_process_memory_conservative_threshold",
983975 Type: "float64",
984- Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryConservativeThreshold),
976+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryConservativeThreshold),
985977 Description: "Network conservative memory threshold (0-1, custom profile only)",
986978 Range: "0-1",
987979 Section: SectionPerformance,
988980 },
989981 {
990- Key: "performance.network_process_memory_strict_threshold",
982+ Key: "engine.webkit.network_process_memory_strict_threshold",
991983 Type: "float64",
992- Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryStrictThreshold),
984+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryStrictThreshold),
993985 Description: "Network strict memory threshold (0-1, custom profile only)",
994986 Range: "0-1",
995987 Section: SectionPerformance,
@@ -997,12 +989,12 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn
997989 }
998990 }
999991
1000-func (*SchemaProvider) getRuntimeKeys(_ *Config) []entity.ConfigKeyInfo {
992+func (*SchemaProvider) getRuntimeKeys(defaults *Config) []entity.ConfigKeyInfo {
1001993 return []entity.ConfigKeyInfo{
1002994 {
1003- Key: "runtime.prefix",
995+ Key: "engine.webkit.prefix",
1004996 Type: "string",
1005- Default: "(empty)",
997+ Default: defaults.Engine.WebKit.Prefix,
1006998 Description: "Custom runtime prefix for WebKitGTK/GTK",
1007999 Section: SectionRuntime,
10081000 },
@@ -712,81 +712,73 @@ func (*SchemaProvider) getClipboardKeys(defaults *Config) []entity.ConfigKeyInfo
712 func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {712 func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {
713 return []entity.ConfigKeyInfo{713 return []entity.ConfigKeyInfo{
714 {714 {
715- Key: "rendering.mode",715+ Key: "engine.webkit.gsk_renderer",
716 Type: "string",716 Type: "string",
717- Default: string(defaults.Rendering.Mode),717+ Default: string(defaults.Engine.WebKit.GSKRenderer),
718- Description: "GPU/CPU rendering selection for WebKit",
719- Values: []string{"auto", "gpu", "cpu"},
720- Section: SectionRendering,
721- },
722- {
723- Key: "rendering.gsk_renderer",
724- Type: "string",
725- Default: string(defaults.Rendering.GSKRenderer),
726 Description: "GTK Scene Kit renderer selection",718 Description: "GTK Scene Kit renderer selection",
727 Values: []string{"auto", "opengl", "vulkan", "cairo"},719 Values: []string{"auto", "opengl", "vulkan", "cairo"},
728 Section: SectionRendering,720 Section: SectionRendering,
729 },721 },
730 {722 {
731- Key: "rendering.disable_dmabuf_renderer",723+ Key: "engine.webkit.disable_dmabuf_renderer",
732 Type: "bool",724 Type: "bool",
733- Default: fmt.Sprintf("%t", defaults.Rendering.DisableDMABufRenderer),725+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableDMABufRenderer),
734 Description: "Disable DMA-BUF accelerated renderer",726 Description: "Disable DMA-BUF accelerated renderer",
735 Section: SectionRendering,727 Section: SectionRendering,
736 },728 },
737 {729 {
738- Key: "rendering.force_compositing_mode",730+ Key: "engine.webkit.force_compositing_mode",
739 Type: "bool",731 Type: "bool",
740- Default: fmt.Sprintf("%t", defaults.Rendering.ForceCompositingMode),732+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ForceCompositingMode),
741 Description: "Force accelerated compositing",733 Description: "Force accelerated compositing",
742 Section: SectionRendering,734 Section: SectionRendering,
743 },735 },
744 {736 {
745- Key: "rendering.disable_compositing_mode",737+ Key: "engine.webkit.disable_compositing_mode",
746 Type: "bool",738 Type: "bool",
747- Default: fmt.Sprintf("%t", defaults.Rendering.DisableCompositingMode),739+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableCompositingMode),
748 Description: "Disable accelerated compositing",740 Description: "Disable accelerated compositing",
749 Section: SectionRendering,741 Section: SectionRendering,
750 },742 },
751 {743 {
752- Key: "rendering.disable_mipmaps",744+ Key: "engine.webkit.disable_mipmaps",
753 Type: "bool",745 Type: "bool",
754- Default: fmt.Sprintf("%t", defaults.Rendering.DisableMipmaps),746+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableMipmaps),
755 Description: "Disable mipmap generation",747 Description: "Disable mipmap generation",
756 Section: SectionRendering,748 Section: SectionRendering,
757 },749 },
758 {750 {
759- Key: "rendering.prefer_gl",751+ Key: "engine.webkit.prefer_gl",
760 Type: "bool",752 Type: "bool",
761- Default: fmt.Sprintf("%t", defaults.Rendering.PreferGL),753+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.PreferGL),
762 Description: "Force OpenGL over GLES",754 Description: "Force OpenGL over GLES",
763 Section: SectionRendering,755 Section: SectionRendering,
764 },756 },
765 {757 {
766- Key: "rendering.draw_compositing_indicators",758+ Key: "engine.webkit.draw_compositing_indicators",
767 Type: "bool",759 Type: "bool",
768- Default: fmt.Sprintf("%t", defaults.Rendering.DrawCompositingIndicators),760+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DrawCompositingIndicators),
769 Description: "Show compositing layer borders",761 Description: "Show compositing layer borders",
770 Section: SectionRendering,762 Section: SectionRendering,
771 },763 },
772 {764 {
773- Key: "rendering.show_fps",765+ Key: "engine.webkit.show_fps",
774 Type: "bool",766 Type: "bool",
775- Default: fmt.Sprintf("%t", defaults.Rendering.ShowFPS),767+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ShowFPS),
776 Description: "Display WebKit FPS counter",768 Description: "Display WebKit FPS counter",
777 Section: SectionRendering,769 Section: SectionRendering,
778 },770 },
779 {771 {
780- Key: "rendering.sample_memory",772+ Key: "engine.webkit.sample_memory",
781 Type: "bool",773 Type: "bool",
782- Default: fmt.Sprintf("%t", defaults.Rendering.SampleMemory),774+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SampleMemory),
783 Description: "Enable memory sampling",775 Description: "Enable memory sampling",
784 Section: SectionRendering,776 Section: SectionRendering,
785 },777 },
786 {778 {
787- Key: "rendering.debug_frames",779+ Key: "engine.webkit.debug_frames",
788 Type: "bool",780 Type: "bool",
789- Default: fmt.Sprintf("%t", defaults.Rendering.DebugFrames),781+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DebugFrames),
790 Description: "Enable frame timing debug output",782 Description: "Enable frame timing debug output",
791 Section: SectionRendering,783 Section: SectionRendering,
792 },784 },
@@ -884,112 +876,112 @@ func (*SchemaProvider) getDebugKeys(defaults *Config) []entity.ConfigKeyInfo {
884 func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {876 func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {
885 return []entity.ConfigKeyInfo{877 return []entity.ConfigKeyInfo{
886 {878 {
887- Key: "performance.profile",879+ Key: "engine.profile",
888 Type: "string",880 Type: "string",
889- Default: string(defaults.Performance.Profile),881+ Default: string(defaults.Engine.Profile),
890 Description: "Performance profile preset (custom enables manual tuning)",882 Description: "Performance profile preset (custom enables manual tuning)",
891 Values: []string{"default", "lite", "max", "custom"},883 Values: []string{"default", "lite", "max", "custom"},
892 Section: SectionPerformance,884 Section: SectionPerformance,
893 },885 },
894 {886 {
895- Key: "performance.zoom_cache_size",887+ Key: "engine.zoom_cache_size",
896 Type: "int",888 Type: "int",
897- Default: fmt.Sprintf("%d", defaults.Performance.ZoomCacheSize),889+ Default: fmt.Sprintf("%d", defaults.Engine.ZoomCacheSize),
898 Description: "Number of domain zoom levels to cache",890 Description: "Number of domain zoom levels to cache",
899 Range: ">=0",891 Range: ">=0",
900 Section: SectionPerformance,892 Section: SectionPerformance,
901 },893 },
902 {894 {
903- Key: "performance.webview_pool_prewarm_count",895+ Key: "engine.pool_prewarm_count",
904 Type: "int",896 Type: "int",
905- Default: fmt.Sprintf("%d", defaults.Performance.WebViewPoolPrewarmCount),897+ Default: fmt.Sprintf("%d", defaults.Engine.PoolPrewarmCount),
906 Description: "WebViews to pre-create at startup",898 Description: "WebViews to pre-create at startup",
907 Range: ">=0",899 Range: ">=0",
908 Section: SectionPerformance,900 Section: SectionPerformance,
909 },901 },
910 {902 {
911- Key: "performance.skia_cpu_painting_threads",903+ Key: "engine.webkit.skia_cpu_painting_threads",
912 Type: "int",904 Type: "int",
913- Default: fmt.Sprintf("%d", defaults.Performance.SkiaCPUPaintingThreads),905+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaCPUPaintingThreads),
914 Description: "Skia CPU rendering threads (0=unset, custom profile only)",906 Description: "Skia CPU rendering threads (0=unset, custom profile only)",
915 Range: ">=0",907 Range: ">=0",
916 Section: SectionPerformance,908 Section: SectionPerformance,
917 },909 },
918 {910 {
919- Key: "performance.skia_gpu_painting_threads",911+ Key: "engine.webkit.skia_gpu_painting_threads",
920 Type: "int",912 Type: "int",
921- Default: fmt.Sprintf("%d", defaults.Performance.SkiaGPUPaintingThreads),913+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaGPUPaintingThreads),
922 Description: "Skia GPU rendering threads (-1=unset, 0=disable, custom profile only)",914 Description: "Skia GPU rendering threads (-1=unset, 0=disable, custom profile only)",
923 Range: ">=-1",915 Range: ">=-1",
924 Section: SectionPerformance,916 Section: SectionPerformance,
925 },917 },
926 {918 {
927- Key: "performance.skia_enable_cpu_rendering",919+ Key: "engine.webkit.skia_enable_cpu_rendering",
928 Type: "bool",920 Type: "bool",
929- Default: fmt.Sprintf("%t", defaults.Performance.SkiaEnableCPURendering),921+ Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SkiaEnableCPURendering),
930 Description: "Force CPU rendering (custom profile only)",922 Description: "Force CPU rendering (custom profile only)",
931 Section: SectionPerformance,923 Section: SectionPerformance,
932 },924 },
933 {925 {
934- Key: "performance.web_process_memory_limit_mb",926+ Key: "engine.webkit.web_process_memory_limit_mb",
935 Type: "int",927 Type: "int",
936- Default: fmt.Sprintf("%d", defaults.Performance.WebProcessMemoryLimitMB),928+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.WebProcessMemoryLimitMB),
937 Description: "Web process memory limit in MB (0=unset, custom profile only)",929 Description: "Web process memory limit in MB (0=unset, custom profile only)",
938 Range: ">=0",930 Range: ">=0",
939 Section: SectionPerformance,931 Section: SectionPerformance,
940 },932 },
941 {933 {
942- Key: "performance.web_process_memory_poll_interval_sec",934+ Key: "engine.webkit.web_process_memory_poll_interval_sec",
943 Type: "float64",935 Type: "float64",
944- Default: fmt.Sprintf("%.1f", defaults.Performance.WebProcessMemoryPollIntervalSec),936+ Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.WebProcessMemoryPollIntervalSec),
945 Description: "Memory check interval in seconds (0=default 30s, custom profile only)",937 Description: "Memory check interval in seconds (0=default 30s, custom profile only)",
946 Range: ">=0",938 Range: ">=0",
947 Section: SectionPerformance,939 Section: SectionPerformance,
948 },940 },
949 {941 {
950- Key: "performance.web_process_memory_conservative_threshold",942+ Key: "engine.webkit.web_process_memory_conservative_threshold",
951 Type: "float64",943 Type: "float64",
952- Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryConservativeThreshold),944+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryConservativeThreshold),
953 Description: "Conservative memory cleanup threshold (0-1, custom profile only)",945 Description: "Conservative memory cleanup threshold (0-1, custom profile only)",
954 Range: "0-1",946 Range: "0-1",
955 Section: SectionPerformance,947 Section: SectionPerformance,
956 },948 },
957 {949 {
958- Key: "performance.web_process_memory_strict_threshold",950+ Key: "engine.webkit.web_process_memory_strict_threshold",
959 Type: "float64",951 Type: "float64",
960- Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryStrictThreshold),952+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryStrictThreshold),
961 Description: "Strict memory cleanup threshold (0-1, custom profile only)",953 Description: "Strict memory cleanup threshold (0-1, custom profile only)",
962 Range: "0-1",954 Range: "0-1",
963 Section: SectionPerformance,955 Section: SectionPerformance,
964 },956 },
965 {957 {
966- Key: "performance.network_process_memory_limit_mb",958+ Key: "engine.webkit.network_process_memory_limit_mb",
967 Type: "int",959 Type: "int",
968- Default: fmt.Sprintf("%d", defaults.Performance.NetworkProcessMemoryLimitMB),960+ Default: fmt.Sprintf("%d", defaults.Engine.WebKit.NetworkProcessMemoryLimitMB),
969 Description: "Network process memory limit in MB (0=unset, custom profile only)",961 Description: "Network process memory limit in MB (0=unset, custom profile only)",
970 Range: ">=0",962 Range: ">=0",
971 Section: SectionPerformance,963 Section: SectionPerformance,
972 },964 },
973 {965 {
974- Key: "performance.network_process_memory_poll_interval_sec",966+ Key: "engine.webkit.network_process_memory_poll_interval_sec",
975 Type: "float64",967 Type: "float64",
976- Default: fmt.Sprintf("%.1f", defaults.Performance.NetworkProcessMemoryPollIntervalSec),968+ Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.NetworkProcessMemoryPollIntervalSec),
977 Description: "Network memory check interval in seconds (custom profile only)",969 Description: "Network memory check interval in seconds (custom profile only)",
978 Range: ">=0",970 Range: ">=0",
979 Section: SectionPerformance,971 Section: SectionPerformance,
980 },972 },
981 {973 {
982- Key: "performance.network_process_memory_conservative_threshold",974+ Key: "engine.webkit.network_process_memory_conservative_threshold",
983 Type: "float64",975 Type: "float64",
984- Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryConservativeThreshold),976+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryConservativeThreshold),
985 Description: "Network conservative memory threshold (0-1, custom profile only)",977 Description: "Network conservative memory threshold (0-1, custom profile only)",
986 Range: "0-1",978 Range: "0-1",
987 Section: SectionPerformance,979 Section: SectionPerformance,
988 },980 },
989 {981 {
990- Key: "performance.network_process_memory_strict_threshold",982+ Key: "engine.webkit.network_process_memory_strict_threshold",
991 Type: "float64",983 Type: "float64",
992- Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryStrictThreshold),984+ Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryStrictThreshold),
993 Description: "Network strict memory threshold (0-1, custom profile only)",985 Description: "Network strict memory threshold (0-1, custom profile only)",
994 Range: "0-1",986 Range: "0-1",
995 Section: SectionPerformance,987 Section: SectionPerformance,
@@ -997,12 +989,12 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn
997 }989 }
998 }990 }
999 991
1000-func (*SchemaProvider) getRuntimeKeys(_ *Config) []entity.ConfigKeyInfo {992+func (*SchemaProvider) getRuntimeKeys(defaults *Config) []entity.ConfigKeyInfo {
1001 return []entity.ConfigKeyInfo{993 return []entity.ConfigKeyInfo{
1002 {994 {
1003- Key: "runtime.prefix",995+ Key: "engine.webkit.prefix",
1004 Type: "string",996 Type: "string",
1005- Default: "(empty)",997+ Default: defaults.Engine.WebKit.Prefix,
1006 Description: "Custom runtime prefix for WebKitGTK/GTK",998 Description: "Custom runtime prefix for WebKitGTK/GTK",
1007 Section: SectionRuntime,999 Section: SectionRuntime,
1008 },1000 },
M internal/infrastructure/config/validation.go +21 -29
@@ -379,30 +379,21 @@ func validateOmnibox(config *Config) []string {
379379 func validateRendering(config *Config) []string {
380380 var validationErrors []string
381381
382- switch config.Rendering.Mode {
383- case RenderingModeAuto, RenderingModeGPU, RenderingModeCPU, "":
384- default:
385- validationErrors = append(
386- validationErrors,
387- fmt.Sprintf("rendering.mode must be one of: auto, gpu, cpu (got: %s)", config.Rendering.Mode),
388- )
389- }
390-
391- switch config.Rendering.GSKRenderer {
382+ switch config.Engine.WebKit.GSKRenderer {
392383 case GSKRendererAuto, GSKRendererOpenGL, GSKRendererVulkan, GSKRendererCairo, "":
393384 default:
394385 validationErrors = append(
395386 validationErrors,
396387 fmt.Sprintf(
397- "rendering.gsk_renderer must be one of: auto, opengl, vulkan, cairo (got: %s)",
398- config.Rendering.GSKRenderer,
388+ "engine.webkit.gsk_renderer must be one of: auto, opengl, vulkan, cairo (got: %s)",
389+ config.Engine.WebKit.GSKRenderer,
399390 ),
400391 )
401392 }
402393
403- if config.Rendering.ForceCompositingMode && config.Rendering.DisableCompositingMode {
394+ if config.Engine.WebKit.ForceCompositingMode && config.Engine.WebKit.DisableCompositingMode {
404395 validationErrors = append(validationErrors,
405- "rendering.force_compositing_mode and rendering.disable_compositing_mode cannot both be true",
396+ "engine.webkit.force_compositing_mode and engine.webkit.disable_compositing_mode cannot both be true",
406397 )
407398 }
408399
@@ -410,13 +401,13 @@ func validateRendering(config *Config) []string {
410401 }
411402
412403 func validatePrivacy(config *Config) []string {
413- switch config.Privacy.CookiePolicy {
404+ switch config.Engine.CookiePolicy {
414405 case CookiePolicyAlways, CookiePolicyNoThirdParty, CookiePolicyNever, "":
415406 return nil
416407 default:
417408 return []string{fmt.Sprintf(
418- "privacy.cookie_policy must be one of: always, no_third_party, never (got: %s)",
419- config.Privacy.CookiePolicy,
409+ "engine.cookie_policy must be one of: always, no_third_party, never (got: %s)",
410+ config.Engine.CookiePolicy,
420411 )}
421412 }
422413 }
@@ -451,39 +442,40 @@ func validatePerformanceProfile(config *Config) []string {
451442 var validationErrors []string
452443
453444 // Validate profile name
454- if !IsValidPerformanceProfile(config.Performance.Profile) {
445+ if !IsValidPerformanceProfile(config.Engine.Profile) {
455446 validationErrors = append(validationErrors, fmt.Sprintf(
456- "performance.profile must be one of: default, lite, max, custom (got: %s)",
457- config.Performance.Profile,
447+ "engine.profile must be one of: default, lite, max, custom (got: %s)",
448+ config.Engine.Profile,
458449 ))
459450 }
460451
461452 // When profile is not "custom", warn if individual fields are set
462453 // (they will be ignored in favor of profile-computed values)
463- if config.Performance.Profile != ProfileCustom && config.Performance.Profile != "" {
464- if HasCustomPerformanceFields(&config.Performance) {
454+ if config.Engine.Profile != ProfileCustom && config.Engine.Profile != "" {
455+ perfCfg := PerformanceConfigFromEngine(&config.Engine)
456+ if HasCustomPerformanceFields(&perfCfg) {
465457 validationErrors = append(validationErrors,
466- "performance tuning fields (skia_*, *_memory_*) are ignored when profile is not 'custom'; "+
458+ "engine performance tuning fields (skia_*, *_memory_*) are ignored when profile is not 'custom'; "+
467459 "set profile = \"custom\" to use individual field values",
468460 )
469461 }
470462 }
471463
472464 // Validate memory pressure threshold ordering: WebKit requires conservative < strict
473- webCons := config.Performance.WebProcessMemoryConservativeThreshold
474- webStrict := config.Performance.WebProcessMemoryStrictThreshold
465+ webCons := config.Engine.WebKit.WebProcessMemoryConservativeThreshold
466+ webStrict := config.Engine.WebKit.WebProcessMemoryStrictThreshold
475467 if webCons > 0 && webStrict > 0 && webCons >= webStrict {
476468 validationErrors = append(validationErrors,
477- "performance.web_process_memory_conservative_threshold must be less than "+
469+ "engine.webkit.web_process_memory_conservative_threshold must be less than "+
478470 "web_process_memory_strict_threshold when both are set",
479471 )
480472 }
481473
482- netCons := config.Performance.NetworkProcessMemoryConservativeThreshold
483- netStrict := config.Performance.NetworkProcessMemoryStrictThreshold
474+ netCons := config.Engine.WebKit.NetworkProcessMemoryConservativeThreshold
475+ netStrict := config.Engine.WebKit.NetworkProcessMemoryStrictThreshold
484476 if netCons > 0 && netStrict > 0 && netCons >= netStrict {
485477 validationErrors = append(validationErrors,
486- "performance.network_process_memory_conservative_threshold must be less than "+
478+ "engine.webkit.network_process_memory_conservative_threshold must be less than "+
487479 "network_process_memory_strict_threshold when both are set",
488480 )
489481 }
@@ -379,30 +379,21 @@ func validateOmnibox(config *Config) []string {
379 func validateRendering(config *Config) []string {379 func validateRendering(config *Config) []string {
380 var validationErrors []string380 var validationErrors []string
381 381
382- switch config.Rendering.Mode {382+ switch config.Engine.WebKit.GSKRenderer {
383- case RenderingModeAuto, RenderingModeGPU, RenderingModeCPU, "":
384- default:
385- validationErrors = append(
386- validationErrors,
387- fmt.Sprintf("rendering.mode must be one of: auto, gpu, cpu (got: %s)", config.Rendering.Mode),
388- )
389- }
390-
391- switch config.Rendering.GSKRenderer {
392 case GSKRendererAuto, GSKRendererOpenGL, GSKRendererVulkan, GSKRendererCairo, "":383 case GSKRendererAuto, GSKRendererOpenGL, GSKRendererVulkan, GSKRendererCairo, "":
393 default:384 default:
394 validationErrors = append(385 validationErrors = append(
395 validationErrors,386 validationErrors,
396 fmt.Sprintf(387 fmt.Sprintf(
397- "rendering.gsk_renderer must be one of: auto, opengl, vulkan, cairo (got: %s)",388+ "engine.webkit.gsk_renderer must be one of: auto, opengl, vulkan, cairo (got: %s)",
398- config.Rendering.GSKRenderer,389+ config.Engine.WebKit.GSKRenderer,
399 ),390 ),
400 )391 )
401 }392 }
402 393
403- if config.Rendering.ForceCompositingMode && config.Rendering.DisableCompositingMode {394+ if config.Engine.WebKit.ForceCompositingMode && config.Engine.WebKit.DisableCompositingMode {
404 validationErrors = append(validationErrors,395 validationErrors = append(validationErrors,
405- "rendering.force_compositing_mode and rendering.disable_compositing_mode cannot both be true",396+ "engine.webkit.force_compositing_mode and engine.webkit.disable_compositing_mode cannot both be true",
406 )397 )
407 }398 }
408 399
@@ -410,13 +401,13 @@ func validateRendering(config *Config) []string {
410 }401 }
411 402
412 func validatePrivacy(config *Config) []string {403 func validatePrivacy(config *Config) []string {
413- switch config.Privacy.CookiePolicy {404+ switch config.Engine.CookiePolicy {
414 case CookiePolicyAlways, CookiePolicyNoThirdParty, CookiePolicyNever, "":405 case CookiePolicyAlways, CookiePolicyNoThirdParty, CookiePolicyNever, "":
415 return nil406 return nil
416 default:407 default:
417 return []string{fmt.Sprintf(408 return []string{fmt.Sprintf(
418- "privacy.cookie_policy must be one of: always, no_third_party, never (got: %s)",409+ "engine.cookie_policy must be one of: always, no_third_party, never (got: %s)",
419- config.Privacy.CookiePolicy,410+ config.Engine.CookiePolicy,
420 )}411 )}
421 }412 }
422 }413 }
@@ -451,39 +442,40 @@ func validatePerformanceProfile(config *Config) []string {
451 var validationErrors []string442 var validationErrors []string
452 443
453 // Validate profile name444 // Validate profile name
454- if !IsValidPerformanceProfile(config.Performance.Profile) {445+ if !IsValidPerformanceProfile(config.Engine.Profile) {
455 validationErrors = append(validationErrors, fmt.Sprintf(446 validationErrors = append(validationErrors, fmt.Sprintf(
456- "performance.profile must be one of: default, lite, max, custom (got: %s)",447+ "engine.profile must be one of: default, lite, max, custom (got: %s)",
457- config.Performance.Profile,448+ config.Engine.Profile,
458 ))449 ))
459 }450 }
460 451
461 // When profile is not "custom", warn if individual fields are set452 // When profile is not "custom", warn if individual fields are set
462 // (they will be ignored in favor of profile-computed values)453 // (they will be ignored in favor of profile-computed values)
463- if config.Performance.Profile != ProfileCustom && config.Performance.Profile != "" {454+ if config.Engine.Profile != ProfileCustom && config.Engine.Profile != "" {
464- if HasCustomPerformanceFields(&config.Performance) {455+ perfCfg := PerformanceConfigFromEngine(&config.Engine)
456+ if HasCustomPerformanceFields(&perfCfg) {
465 validationErrors = append(validationErrors,457 validationErrors = append(validationErrors,
466- "performance tuning fields (skia_*, *_memory_*) are ignored when profile is not 'custom'; "+458+ "engine performance tuning fields (skia_*, *_memory_*) are ignored when profile is not 'custom'; "+
467 "set profile = \"custom\" to use individual field values",459 "set profile = \"custom\" to use individual field values",
468 )460 )
469 }461 }
470 }462 }
471 463
472 // Validate memory pressure threshold ordering: WebKit requires conservative < strict464 // Validate memory pressure threshold ordering: WebKit requires conservative < strict
473- webCons := config.Performance.WebProcessMemoryConservativeThreshold465+ webCons := config.Engine.WebKit.WebProcessMemoryConservativeThreshold
474- webStrict := config.Performance.WebProcessMemoryStrictThreshold466+ webStrict := config.Engine.WebKit.WebProcessMemoryStrictThreshold
475 if webCons > 0 && webStrict > 0 && webCons >= webStrict {467 if webCons > 0 && webStrict > 0 && webCons >= webStrict {
476 validationErrors = append(validationErrors,468 validationErrors = append(validationErrors,
477- "performance.web_process_memory_conservative_threshold must be less than "+469+ "engine.webkit.web_process_memory_conservative_threshold must be less than "+
478 "web_process_memory_strict_threshold when both are set",470 "web_process_memory_strict_threshold when both are set",
479 )471 )
480 }472 }
481 473
482- netCons := config.Performance.NetworkProcessMemoryConservativeThreshold474+ netCons := config.Engine.WebKit.NetworkProcessMemoryConservativeThreshold
483- netStrict := config.Performance.NetworkProcessMemoryStrictThreshold475+ netStrict := config.Engine.WebKit.NetworkProcessMemoryStrictThreshold
484 if netCons > 0 && netStrict > 0 && netCons >= netStrict {476 if netCons > 0 && netStrict > 0 && netCons >= netStrict {
485 validationErrors = append(validationErrors,477 validationErrors = append(validationErrors,
486- "performance.network_process_memory_conservative_threshold must be less than "+478+ "engine.webkit.network_process_memory_conservative_threshold must be less than "+
487 "network_process_memory_strict_threshold when both are set",479 "network_process_memory_strict_threshold when both are set",
488 )480 )
489 }481 }
M internal/infrastructure/config/validation_test.go +3 -3
@@ -7,7 +7,7 @@ import (
77 "github.com/stretchr/testify/require"
88 )
99
10-func TestValidateConfig_PrivacyCookiePolicy(t *testing.T) {
10+func TestValidateConfig_EngineCookiePolicy(t *testing.T) {
1111 tests := []struct {
1212 name string
1313 cookiePolicy CookiePolicy
@@ -22,12 +22,12 @@ func TestValidateConfig_PrivacyCookiePolicy(t *testing.T) {
2222 for _, tt := range tests {
2323 t.Run(tt.name, func(t *testing.T) {
2424 cfg := DefaultConfig()
25- cfg.Privacy.CookiePolicy = tt.cookiePolicy
25+ cfg.Engine.CookiePolicy = tt.cookiePolicy
2626
2727 err := validateConfig(cfg)
2828 if tt.wantErr {
2929 require.Error(t, err)
30- assert.Contains(t, err.Error(), "privacy.cookie_policy")
30+ assert.Contains(t, err.Error(), "engine.cookie_policy")
3131 return
3232 }
3333 require.NoError(t, err)
@@ -7,7 +7,7 @@ import (
7 "github.com/stretchr/testify/require"7 "github.com/stretchr/testify/require"
8 )8 )
9 9
10-func TestValidateConfig_PrivacyCookiePolicy(t *testing.T) {10+func TestValidateConfig_EngineCookiePolicy(t *testing.T) {
11 tests := []struct {11 tests := []struct {
12 name string12 name string
13 cookiePolicy CookiePolicy13 cookiePolicy CookiePolicy
@@ -22,12 +22,12 @@ func TestValidateConfig_PrivacyCookiePolicy(t *testing.T) {
22 for _, tt := range tests {22 for _, tt := range tests {
23 t.Run(tt.name, func(t *testing.T) {23 t.Run(tt.name, func(t *testing.T) {
24 cfg := DefaultConfig()24 cfg := DefaultConfig()
25- cfg.Privacy.CookiePolicy = tt.cookiePolicy25+ cfg.Engine.CookiePolicy = tt.cookiePolicy
26 26
27 err := validateConfig(cfg)27 err := validateConfig(cfg)
28 if tt.wantErr {28 if tt.wantErr {
29 require.Error(t, err)29 require.Error(t, err)
30- assert.Contains(t, err.Error(), "privacy.cookie_policy")30+ assert.Contains(t, err.Error(), "engine.cookie_policy")
31 return31 return
32 }32 }
33 require.NoError(t, err)33 require.NoError(t, err)
M internal/infrastructure/config/webui_gateway.go +6 -6
@@ -80,16 +80,16 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUI
8080
8181 // Performance profile (requires restart to take effect)
8282 if cfg.Performance.Profile != "" {
83- current.Performance.Profile = PerformanceProfile(cfg.Performance.Profile)
83+ current.Engine.Profile = PerformanceProfile(cfg.Performance.Profile)
8484 }
8585
8686 // Custom performance fields (only used when profile is "custom")
8787 if cfg.Performance.Profile == string(ProfileCustom) {
88- current.Performance.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)
89- current.Performance.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)
90- current.Performance.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)
91- current.Performance.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)
92- current.Performance.WebViewPoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)
88+ current.Engine.WebKit.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)
89+ current.Engine.WebKit.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)
90+ current.Engine.WebKit.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)
91+ current.Engine.WebKit.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)
92+ current.Engine.PoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)
9393 }
9494
9595 return g.mgr.Save(current)
@@ -80,16 +80,16 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUI
80 80
81 // Performance profile (requires restart to take effect)81 // Performance profile (requires restart to take effect)
82 if cfg.Performance.Profile != "" {82 if cfg.Performance.Profile != "" {
83- current.Performance.Profile = PerformanceProfile(cfg.Performance.Profile)83+ current.Engine.Profile = PerformanceProfile(cfg.Performance.Profile)
84 }84 }
85 85
86 // Custom performance fields (only used when profile is "custom")86 // Custom performance fields (only used when profile is "custom")
87 if cfg.Performance.Profile == string(ProfileCustom) {87 if cfg.Performance.Profile == string(ProfileCustom) {
88- current.Performance.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)88+ current.Engine.WebKit.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)
89- current.Performance.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)89+ current.Engine.WebKit.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)
90- current.Performance.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)90+ current.Engine.WebKit.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)
91- current.Performance.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)91+ current.Engine.WebKit.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)
92- current.Performance.WebViewPoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)92+ current.Engine.PoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)
93 }93 }
94 94
95 return g.mgr.Save(current)95 return g.mgr.Save(current)
M internal/infrastructure/webkit/context.go +12 -13
@@ -6,7 +6,6 @@ import (
66 "path/filepath"
77 "sync"
88
9- "github.com/bnema/dumber/internal/application/port"
109 "github.com/bnema/dumber/internal/logging"
1110 "github.com/bnema/puregotk-webkit/webkit"
1211 "github.com/rs/zerolog"
@@ -36,17 +35,17 @@ type WebKitContext struct {
3635 // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data.
3736 // This MUST be called before creating any WebViews to ensure they use persistent storage.
3837 func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) {
39- return NewWebKitContextWithOptions(ctx, port.WebKitContextOptions{
38+ return NewWebKitContextWithOptions(ctx, webKitContextOptions{
4039 DataDir: dataDir,
4140 CacheDir: cacheDir,
42- CookiePolicy: port.WebKitCookiePolicyNoThirdParty,
41+ CookiePolicy: cookiePolicyNoThirdParty,
4342 ITPEnabled: true,
4443 })
4544 }
4645
4746 // NewWebKitContextWithOptions creates and initializes a WebKitContext with the given options.
4847 // This allows configuring memory pressure settings for web and network processes.
49-func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOptions) (*WebKitContext, error) {
48+func NewWebKitContextWithOptions(ctx context.Context, opts webKitContextOptions) (*WebKitContext, error) {
5049 log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger()
5150
5251 if opts.DataDir == "" {
@@ -114,7 +113,7 @@ func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOpt
114113 }
115114
116115 // initNetworkSession creates and configures the persistent network session.
117-func (c *WebKitContext) initNetworkSession(opts port.WebKitContextOptions) error {
116+func (c *WebKitContext) initNetworkSession(opts webKitContextOptions) error {
118117 // Create persistent network session
119118 session := webkit.NewNetworkSession(&c.dataDir, &c.cacheDir)
120119 if session == nil {
@@ -188,16 +187,16 @@ func (c *WebKitContext) initNetworkSession(opts port.WebKitContextOptions) error
188187 return nil
189188 }
190189
191-func mapCookiePolicy(policy port.WebKitCookiePolicy) (webkit.CookieAcceptPolicy, string) {
190+func mapCookiePolicy(policy cookiePolicy) (webkit.CookieAcceptPolicy, string) {
192191 switch policy {
193- case port.WebKitCookiePolicyAlways:
194- return webkit.CookiePolicyAcceptAlwaysValue, string(port.WebKitCookiePolicyAlways)
195- case port.WebKitCookiePolicyNever:
196- return webkit.CookiePolicyAcceptNeverValue, string(port.WebKitCookiePolicyNever)
197- case port.WebKitCookiePolicyNoThirdParty, "":
198- return webkit.CookiePolicyAcceptNoThirdPartyValue, string(port.WebKitCookiePolicyNoThirdParty)
192+ case cookiePolicyAlways:
193+ return webkit.CookiePolicyAcceptAlwaysValue, string(cookiePolicyAlways)
194+ case cookiePolicyNever:
195+ return webkit.CookiePolicyAcceptNeverValue, string(cookiePolicyNever)
196+ case cookiePolicyNoThirdParty, "":
197+ return webkit.CookiePolicyAcceptNoThirdPartyValue, string(cookiePolicyNoThirdParty)
199198 default:
200- return webkit.CookiePolicyAcceptNoThirdPartyValue, string(port.WebKitCookiePolicyNoThirdParty)
199+ return webkit.CookiePolicyAcceptNoThirdPartyValue, string(cookiePolicyNoThirdParty)
201200 }
202201 }
203202
@@ -6,7 +6,6 @@ import (
6 "path/filepath"6 "path/filepath"
7 "sync"7 "sync"
8 8
9- "github.com/bnema/dumber/internal/application/port"
10 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
11 "github.com/bnema/puregotk-webkit/webkit"10 "github.com/bnema/puregotk-webkit/webkit"
12 "github.com/rs/zerolog"11 "github.com/rs/zerolog"
@@ -36,17 +35,17 @@ type WebKitContext struct {
36 // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data.35 // The dataDir and cacheDir are used for cookie storage, cache, and other persistent data.
37 // This MUST be called before creating any WebViews to ensure they use persistent storage.36 // This MUST be called before creating any WebViews to ensure they use persistent storage.
38 func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) {37 func NewWebKitContext(ctx context.Context, dataDir, cacheDir string) (*WebKitContext, error) {
39- return NewWebKitContextWithOptions(ctx, port.WebKitContextOptions{38+ return NewWebKitContextWithOptions(ctx, webKitContextOptions{
40 DataDir: dataDir,39 DataDir: dataDir,
41 CacheDir: cacheDir,40 CacheDir: cacheDir,
42- CookiePolicy: port.WebKitCookiePolicyNoThirdParty,41+ CookiePolicy: cookiePolicyNoThirdParty,
43 ITPEnabled: true,42 ITPEnabled: true,
44 })43 })
45 }44 }
46 45
47 // NewWebKitContextWithOptions creates and initializes a WebKitContext with the given options.46 // NewWebKitContextWithOptions creates and initializes a WebKitContext with the given options.
48 // This allows configuring memory pressure settings for web and network processes.47 // This allows configuring memory pressure settings for web and network processes.
49-func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOptions) (*WebKitContext, error) {48+func NewWebKitContextWithOptions(ctx context.Context, opts webKitContextOptions) (*WebKitContext, error) {
50 log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger()49 log := logging.FromContext(ctx).With().Str("component", "webkit-context").Logger()
51 50
52 if opts.DataDir == "" {51 if opts.DataDir == "" {
@@ -114,7 +113,7 @@ func NewWebKitContextWithOptions(ctx context.Context, opts port.WebKitContextOpt
114 }113 }
115 114
116 // initNetworkSession creates and configures the persistent network session.115 // initNetworkSession creates and configures the persistent network session.
117-func (c *WebKitContext) initNetworkSession(opts port.WebKitContextOptions) error {116+func (c *WebKitContext) initNetworkSession(opts webKitContextOptions) error {
118 // Create persistent network session117 // Create persistent network session
119 session := webkit.NewNetworkSession(&c.dataDir, &c.cacheDir)118 session := webkit.NewNetworkSession(&c.dataDir, &c.cacheDir)
120 if session == nil {119 if session == nil {
@@ -188,16 +187,16 @@ func (c *WebKitContext) initNetworkSession(opts port.WebKitContextOptions) error
188 return nil187 return nil
189 }188 }
190 189
191-func mapCookiePolicy(policy port.WebKitCookiePolicy) (webkit.CookieAcceptPolicy, string) {190+func mapCookiePolicy(policy cookiePolicy) (webkit.CookieAcceptPolicy, string) {
192 switch policy {191 switch policy {
193- case port.WebKitCookiePolicyAlways:192+ case cookiePolicyAlways:
194- return webkit.CookiePolicyAcceptAlwaysValue, string(port.WebKitCookiePolicyAlways)193+ return webkit.CookiePolicyAcceptAlwaysValue, string(cookiePolicyAlways)
195- case port.WebKitCookiePolicyNever:194+ case cookiePolicyNever:
196- return webkit.CookiePolicyAcceptNeverValue, string(port.WebKitCookiePolicyNever)195+ return webkit.CookiePolicyAcceptNeverValue, string(cookiePolicyNever)
197- case port.WebKitCookiePolicyNoThirdParty, "":196+ case cookiePolicyNoThirdParty, "":
198- return webkit.CookiePolicyAcceptNoThirdPartyValue, string(port.WebKitCookiePolicyNoThirdParty)197+ return webkit.CookiePolicyAcceptNoThirdPartyValue, string(cookiePolicyNoThirdParty)
199 default:198 default:
200- return webkit.CookiePolicyAcceptNoThirdPartyValue, string(port.WebKitCookiePolicyNoThirdParty)199+ return webkit.CookiePolicyAcceptNoThirdPartyValue, string(cookiePolicyNoThirdParty)
201 }200 }
202 }201 }
203 202
A internal/infrastructure/webkit/context_options.go +56 -0
@@ -0,0 +1,56 @@
1+package webkit
2+
3+import "github.com/bnema/dumber/internal/application/port"
4+
5+// cookiePolicy controls cookie acceptance behavior for the WebKit NetworkSession.
6+// This is a webkit-internal type; values match port.CookiePolicy string values.
7+type cookiePolicy = port.CookiePolicy
8+
9+const (
10+ cookiePolicyAlways cookiePolicy = port.CookiePolicyAlways
11+ cookiePolicyNoThirdParty cookiePolicy = port.CookiePolicyNoThirdParty
12+ cookiePolicyNever cookiePolicy = port.CookiePolicyNever
13+)
14+
15+// webKitContextOptions configures WebKitContext creation.
16+// This is a webkit-specific options struct that extends EngineOptions with
17+// WebKit-specific fields (e.g. ITPEnabled).
18+type webKitContextOptions struct {
19+ // DataDir is the directory for persistent data (cookies, storage, etc.).
20+ DataDir string
21+
22+ // CacheDir is the directory for cache data.
23+ CacheDir string
24+
25+ // CookiePolicy controls cookie acceptance behavior.
26+ // Empty value means runtime defaults are used.
27+ CookiePolicy cookiePolicy
28+
29+ // ITPEnabled controls Intelligent Tracking Prevention for NetworkSession.
30+ // False means ITP remains disabled unless enabled explicitly.
31+ ITPEnabled bool
32+
33+ // WebProcessMemory configures memory pressure for web processes.
34+ // nil means use WebKit defaults.
35+ WebProcessMemory *port.MemoryPressureConfig
36+
37+ // NetworkProcessMemory configures memory pressure for the network process.
38+ // nil means use WebKit defaults.
39+ NetworkProcessMemory *port.MemoryPressureConfig
40+}
41+
42+// IsWebProcessMemoryConfigured returns true if web process memory settings are configured.
43+func (o *webKitContextOptions) IsWebProcessMemoryConfigured() bool {
44+ if o == nil || o.WebProcessMemory == nil {
45+ return false
46+ }
47+ return o.WebProcessMemory.IsConfigured()
48+}
49+
50+// IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured.
51+func (o *webKitContextOptions) IsNetworkProcessMemoryConfigured() bool {
52+ if o == nil || o.NetworkProcessMemory == nil {
53+ return false
54+ }
55+ return o.NetworkProcessMemory.IsConfigured()
56+}
@@ -0,0 +1,56 @@
1+package webkit
2+
3+import "github.com/bnema/dumber/internal/application/port"
4+
5+// cookiePolicy controls cookie acceptance behavior for the WebKit NetworkSession.
6+// This is a webkit-internal type; values match port.CookiePolicy string values.
7+type cookiePolicy = port.CookiePolicy
8+
9+const (
10+ cookiePolicyAlways cookiePolicy = port.CookiePolicyAlways
11+ cookiePolicyNoThirdParty cookiePolicy = port.CookiePolicyNoThirdParty
12+ cookiePolicyNever cookiePolicy = port.CookiePolicyNever
13+)
14+
15+// webKitContextOptions configures WebKitContext creation.
16+// This is a webkit-specific options struct that extends EngineOptions with
17+// WebKit-specific fields (e.g. ITPEnabled).
18+type webKitContextOptions struct {
19+ // DataDir is the directory for persistent data (cookies, storage, etc.).
20+ DataDir string
21+
22+ // CacheDir is the directory for cache data.
23+ CacheDir string
24+
25+ // CookiePolicy controls cookie acceptance behavior.
26+ // Empty value means runtime defaults are used.
27+ CookiePolicy cookiePolicy
28+
29+ // ITPEnabled controls Intelligent Tracking Prevention for NetworkSession.
30+ // False means ITP remains disabled unless enabled explicitly.
31+ ITPEnabled bool
32+
33+ // WebProcessMemory configures memory pressure for web processes.
34+ // nil means use WebKit defaults.
35+ WebProcessMemory *port.MemoryPressureConfig
36+
37+ // NetworkProcessMemory configures memory pressure for the network process.
38+ // nil means use WebKit defaults.
39+ NetworkProcessMemory *port.MemoryPressureConfig
40+}
41+
42+// IsWebProcessMemoryConfigured returns true if web process memory settings are configured.
43+func (o *webKitContextOptions) IsWebProcessMemoryConfigured() bool {
44+ if o == nil || o.WebProcessMemory == nil {
45+ return false
46+ }
47+ return o.WebProcessMemory.IsConfigured()
48+}
49+
50+// IsNetworkProcessMemoryConfigured returns true if network process memory settings are configured.
51+func (o *webKitContextOptions) IsNetworkProcessMemoryConfigured() bool {
52+ if o == nil || o.NetworkProcessMemory == nil {
53+ return false
54+ }
55+ return o.NetworkProcessMemory.IsConfigured()
56+}
M internal/infrastructure/webkit/engine.go +5 -8
@@ -10,6 +10,7 @@ import (
1010
1111 // Engine implements port.Engine for the WebKit browser engine.
1212 type Engine struct {
13+ ctx context.Context
1314 wkCtx *WebKitContext
1415 settings *SettingsManager
1516 injector *ContentInjector
@@ -25,11 +26,6 @@ type Engine struct {
2526 // Compile-time check that Engine implements port.Engine.
2627 var _ port.Engine = (*Engine)(nil)
2728
28-// Init is a no-op for WebKit — initialization happens in NewEngine.
29-func (*Engine) Init(_ context.Context, _ port.EngineOptions) error {
30- return nil
31-}
32-
3329 // Factory returns the WebViewFactory wrapped as a port.WebViewFactory.
3430 func (e *Engine) Factory() port.WebViewFactory {
3531 return &webViewFactoryAdapter{factory: e.factory}
@@ -37,7 +33,7 @@ func (e *Engine) Factory() port.WebViewFactory {
3733
3834 // Pool returns the WebViewPool wrapped as a port.WebViewPool.
3935 func (e *Engine) Pool() port.WebViewPool {
40- return &webViewPoolAdapter{pool: e.pool, logger: e.logger}
36+ return &webViewPoolAdapter{pool: e.pool, ctx: e.ctx}
4137 }
4238
4339 // ContentInjector returns the ContentInjector implementing port.ContentInjector.
@@ -53,7 +49,7 @@ func (e *Engine) InternalSchemePath() string {
5349 // Close releases all resources held by the engine.
5450 func (e *Engine) Close() error {
5551 if e.pool != nil {
56- e.pool.Close(context.Background())
52+ e.pool.Close(e.ctx)
5753 }
5854 return nil
5955 }
@@ -65,7 +61,7 @@ func (e *Engine) SchemeHandler() port.SchemeHandler {
6561
6662 // MessageRouter returns a port.MessageRouter adapter for the internal MessageRouter.
6763 func (e *Engine) MessageRouter() port.MessageRouter {
68- return &messageRouterAdapter{router: e.messageRouter}
64+ return &messageRouterAdapter{router: e.messageRouter, logger: e.logger}
6965 }
7066
7167 // SettingsApplier returns a port.SettingsApplier adapter for the SettingsManager.
@@ -94,4 +90,5 @@ func (e *Engine) InternalSettings() *SettingsManager { return e.settings
9490 func (e *Engine) InternalInjector() *ContentInjector { return e.injector }
9591 func (e *Engine) InternalMessageRouter() *MessageRouter { return e.messageRouter }
9692 func (e *Engine) InternalPool() *WebViewPool { return e.pool }
93+func (e *Engine) InternalFactory() *WebViewFactory { return e.factory }
9794 func (e *Engine) InternalFilterManager() *filtering.Manager { return e.filterManager }
@@ -10,6 +10,7 @@ import (
10 10
11 // Engine implements port.Engine for the WebKit browser engine.11 // Engine implements port.Engine for the WebKit browser engine.
12 type Engine struct {12 type Engine struct {
13+ ctx context.Context
13 wkCtx *WebKitContext14 wkCtx *WebKitContext
14 settings *SettingsManager15 settings *SettingsManager
15 injector *ContentInjector16 injector *ContentInjector
@@ -25,11 +26,6 @@ type Engine struct {
25 // Compile-time check that Engine implements port.Engine.26 // Compile-time check that Engine implements port.Engine.
26 var _ port.Engine = (*Engine)(nil)27 var _ port.Engine = (*Engine)(nil)
27 28
28-// Init is a no-op for WebKit — initialization happens in NewEngine.
29-func (*Engine) Init(_ context.Context, _ port.EngineOptions) error {
30- return nil
31-}
32-
33 // Factory returns the WebViewFactory wrapped as a port.WebViewFactory.29 // Factory returns the WebViewFactory wrapped as a port.WebViewFactory.
34 func (e *Engine) Factory() port.WebViewFactory {30 func (e *Engine) Factory() port.WebViewFactory {
35 return &webViewFactoryAdapter{factory: e.factory}31 return &webViewFactoryAdapter{factory: e.factory}
@@ -37,7 +33,7 @@ func (e *Engine) Factory() port.WebViewFactory {
37 33
38 // Pool returns the WebViewPool wrapped as a port.WebViewPool.34 // Pool returns the WebViewPool wrapped as a port.WebViewPool.
39 func (e *Engine) Pool() port.WebViewPool {35 func (e *Engine) Pool() port.WebViewPool {
40- return &webViewPoolAdapter{pool: e.pool, logger: e.logger}36+ return &webViewPoolAdapter{pool: e.pool, ctx: e.ctx}
41 }37 }
42 38
43 // ContentInjector returns the ContentInjector implementing port.ContentInjector.39 // ContentInjector returns the ContentInjector implementing port.ContentInjector.
@@ -53,7 +49,7 @@ func (e *Engine) InternalSchemePath() string {
53 // Close releases all resources held by the engine.49 // Close releases all resources held by the engine.
54 func (e *Engine) Close() error {50 func (e *Engine) Close() error {
55 if e.pool != nil {51 if e.pool != nil {
56- e.pool.Close(context.Background())52+ e.pool.Close(e.ctx)
57 }53 }
58 return nil54 return nil
59 }55 }
@@ -65,7 +61,7 @@ func (e *Engine) SchemeHandler() port.SchemeHandler {
65 61
66 // MessageRouter returns a port.MessageRouter adapter for the internal MessageRouter.62 // MessageRouter returns a port.MessageRouter adapter for the internal MessageRouter.
67 func (e *Engine) MessageRouter() port.MessageRouter {63 func (e *Engine) MessageRouter() port.MessageRouter {
68- return &messageRouterAdapter{router: e.messageRouter}64+ return &messageRouterAdapter{router: e.messageRouter, logger: e.logger}
69 }65 }
70 66
71 // SettingsApplier returns a port.SettingsApplier adapter for the SettingsManager.67 // SettingsApplier returns a port.SettingsApplier adapter for the SettingsManager.
@@ -94,4 +90,5 @@ func (e *Engine) InternalSettings() *SettingsManager { return e.settings
94 func (e *Engine) InternalInjector() *ContentInjector { return e.injector }90 func (e *Engine) InternalInjector() *ContentInjector { return e.injector }
95 func (e *Engine) InternalMessageRouter() *MessageRouter { return e.messageRouter }91 func (e *Engine) InternalMessageRouter() *MessageRouter { return e.messageRouter }
96 func (e *Engine) InternalPool() *WebViewPool { return e.pool }92 func (e *Engine) InternalPool() *WebViewPool { return e.pool }
93+func (e *Engine) InternalFactory() *WebViewFactory { return e.factory }
97 func (e *Engine) InternalFilterManager() *filtering.Manager { return e.filterManager }94 func (e *Engine) InternalFilterManager() *filtering.Manager { return e.filterManager }
M internal/infrastructure/webkit/engine_adapters.go +38 -14
@@ -6,6 +6,8 @@ import (
66
77 "github.com/bnema/dumber/internal/application/port"
88 "github.com/bnema/dumber/internal/infrastructure/filtering"
9+ "github.com/bnema/dumber/internal/logging"
10+ "github.com/jwijenbergh/puregotk/v4/gio"
911 "github.com/rs/zerolog"
1012 )
1113
@@ -40,8 +42,8 @@ func (a *webViewFactoryAdapter) CreateRelated(ctx context.Context, parentID port
4042 // WebViewPool methods use *WebView and require a context; this adapter adapts
4143 // the signatures to match the port interface.
4244 type webViewPoolAdapter struct {
43- pool *WebViewPool
44- logger zerolog.Logger
45+ pool *WebViewPool
46+ ctx context.Context
4547 }
4648
4749 func (a *webViewPoolAdapter) Acquire(ctx context.Context) (port.WebView, error) {
@@ -58,16 +60,16 @@ func (a *webViewPoolAdapter) Release(wv port.WebView) {
5860 }
5961 wwv, ok := wv.(*WebView)
6062 if !ok {
61- a.logger.Warn().
63+ logging.FromContext(a.ctx).Warn().
6264 Str("concrete_type", fmt.Sprintf("%T", wv)).
6365 Msg("webViewPoolAdapter.Release: unexpected type, cannot release to pool")
6466 return
6567 }
66- a.pool.Release(context.Background(), wwv)
68+ a.pool.Release(a.ctx, wwv)
6769 }
6870
6971 func (a *webViewPoolAdapter) Prewarm(count int) {
70- a.pool.Prewarm(context.Background(), count)
72+ a.pool.Prewarm(a.ctx, count)
7173 }
7274
7375 func (a *webViewPoolAdapter) Size() int {
@@ -75,7 +77,7 @@ func (a *webViewPoolAdapter) Size() int {
7577 }
7678
7779 func (a *webViewPoolAdapter) Close() {
78- a.pool.Close(context.Background())
80+ a.pool.Close(a.ctx)
7981 }
8082
8183 // --- SchemeHandler adapter ---
@@ -105,12 +107,15 @@ func (a *schemeHandlerAdapter) RegisterScheme(scheme string, _ func(uri string)
105107 // PostMessage is also not directly available on the internal type.
106108 type messageRouterAdapter struct {
107109 router *MessageRouter
110+ logger zerolog.Logger
108111 }
109112
110-func (*messageRouterAdapter) RegisterHandler(_ string, _ func(message string) (string, error)) {
113+func (a *messageRouterAdapter) RegisterHandler(name string, _ func(message string) (string, error)) {
111114 // Internal MessageRouter.RegisterHandler takes a MessageHandler interface, not a plain func.
112115 // Wire up via RegisterHandler(msgType, MessageHandlerFunc{...}) when consumers are migrated.
113- panic("not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly")
116+ a.logger.Warn().
117+ Str("handler", name).
118+ Msg("messageRouterAdapter.RegisterHandler: not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly")
114119 }
115120
116121 func (*messageRouterAdapter) PostMessage(webviewID port.WebViewID, message string) error {
@@ -118,7 +123,7 @@ func (*messageRouterAdapter) PostMessage(webviewID port.WebViewID, message strin
118123 if wv == nil {
119124 return fmt.Errorf("webview %d not found", webviewID)
120125 }
121- wv.RunJavaScript(context.Background(), message, "")
126+ wv.RunJavaScript(context.Background(), message)
122127 return nil
123128 }
124129
@@ -161,9 +166,28 @@ type faviconDatabaseAdapter struct {
161166 wkCtx *WebKitContext
162167 }
163168
164-func (*faviconDatabaseAdapter) GetFaviconAsync(_ string, callback func(port.Texture)) {
165- // FaviconDatabase async lookup is not yet wired through this adapter.
166- // The underlying API is: wkCtx.FaviconDatabase().GetFavicon(uri, callback).
167- // This stub satisfies the interface contract; callers will receive nil.
168- callback(nil)
169+func (a *faviconDatabaseAdapter) GetFaviconAsync(pageURL string, callback func(port.Texture)) {
170+ db := a.wkCtx.FaviconDatabase()
171+ if db == nil {
172+ callback(nil)
173+ return
174+ }
175+
176+ asyncCb := gio.AsyncReadyCallback(func(_ uintptr, resultPtr uintptr, _ uintptr) {
177+ if resultPtr == 0 {
178+ callback(nil)
179+ return
180+ }
181+
182+ result := &gio.AsyncResultBase{Ptr: resultPtr}
183+ texture, err := db.GetFaviconFinish(result)
184+ if err != nil || texture == nil {
185+ callback(nil)
186+ return
187+ }
188+
189+ callback(texture)
190+ })
191+
192+ db.GetFavicon(pageURL, nil, &asyncCb, 0)
169193 }
@@ -6,6 +6,8 @@ import (
6 6
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/infrastructure/filtering"8 "github.com/bnema/dumber/internal/infrastructure/filtering"
9+ "github.com/bnema/dumber/internal/logging"
10+ "github.com/jwijenbergh/puregotk/v4/gio"
9 "github.com/rs/zerolog"11 "github.com/rs/zerolog"
10 )12 )
11 13
@@ -40,8 +42,8 @@ func (a *webViewFactoryAdapter) CreateRelated(ctx context.Context, parentID port
40 // WebViewPool methods use *WebView and require a context; this adapter adapts42 // WebViewPool methods use *WebView and require a context; this adapter adapts
41 // the signatures to match the port interface.43 // the signatures to match the port interface.
42 type webViewPoolAdapter struct {44 type webViewPoolAdapter struct {
43- pool *WebViewPool45+ pool *WebViewPool
44- logger zerolog.Logger46+ ctx context.Context
45 }47 }
46 48
47 func (a *webViewPoolAdapter) Acquire(ctx context.Context) (port.WebView, error) {49 func (a *webViewPoolAdapter) Acquire(ctx context.Context) (port.WebView, error) {
@@ -58,16 +60,16 @@ func (a *webViewPoolAdapter) Release(wv port.WebView) {
58 }60 }
59 wwv, ok := wv.(*WebView)61 wwv, ok := wv.(*WebView)
60 if !ok {62 if !ok {
61- a.logger.Warn().63+ logging.FromContext(a.ctx).Warn().
62 Str("concrete_type", fmt.Sprintf("%T", wv)).64 Str("concrete_type", fmt.Sprintf("%T", wv)).
63 Msg("webViewPoolAdapter.Release: unexpected type, cannot release to pool")65 Msg("webViewPoolAdapter.Release: unexpected type, cannot release to pool")
64 return66 return
65 }67 }
66- a.pool.Release(context.Background(), wwv)68+ a.pool.Release(a.ctx, wwv)
67 }69 }
68 70
69 func (a *webViewPoolAdapter) Prewarm(count int) {71 func (a *webViewPoolAdapter) Prewarm(count int) {
70- a.pool.Prewarm(context.Background(), count)72+ a.pool.Prewarm(a.ctx, count)
71 }73 }
72 74
73 func (a *webViewPoolAdapter) Size() int {75 func (a *webViewPoolAdapter) Size() int {
@@ -75,7 +77,7 @@ func (a *webViewPoolAdapter) Size() int {
75 }77 }
76 78
77 func (a *webViewPoolAdapter) Close() {79 func (a *webViewPoolAdapter) Close() {
78- a.pool.Close(context.Background())80+ a.pool.Close(a.ctx)
79 }81 }
80 82
81 // --- SchemeHandler adapter ---83 // --- SchemeHandler adapter ---
@@ -105,12 +107,15 @@ func (a *schemeHandlerAdapter) RegisterScheme(scheme string, _ func(uri string)
105 // PostMessage is also not directly available on the internal type.107 // PostMessage is also not directly available on the internal type.
106 type messageRouterAdapter struct {108 type messageRouterAdapter struct {
107 router *MessageRouter109 router *MessageRouter
110+ logger zerolog.Logger
108 }111 }
109 112
110-func (*messageRouterAdapter) RegisterHandler(_ string, _ func(message string) (string, error)) {113+func (a *messageRouterAdapter) RegisterHandler(name string, _ func(message string) (string, error)) {
111 // Internal MessageRouter.RegisterHandler takes a MessageHandler interface, not a plain func.114 // Internal MessageRouter.RegisterHandler takes a MessageHandler interface, not a plain func.
112 // Wire up via RegisterHandler(msgType, MessageHandlerFunc{...}) when consumers are migrated.115 // Wire up via RegisterHandler(msgType, MessageHandlerFunc{...}) when consumers are migrated.
113- panic("not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly")116+ a.logger.Warn().
117+ Str("handler", name).
118+ Msg("messageRouterAdapter.RegisterHandler: not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly")
114 }119 }
115 120
116 func (*messageRouterAdapter) PostMessage(webviewID port.WebViewID, message string) error {121 func (*messageRouterAdapter) PostMessage(webviewID port.WebViewID, message string) error {
@@ -118,7 +123,7 @@ func (*messageRouterAdapter) PostMessage(webviewID port.WebViewID, message strin
118 if wv == nil {123 if wv == nil {
119 return fmt.Errorf("webview %d not found", webviewID)124 return fmt.Errorf("webview %d not found", webviewID)
120 }125 }
121- wv.RunJavaScript(context.Background(), message, "")126+ wv.RunJavaScript(context.Background(), message)
122 return nil127 return nil
123 }128 }
124 129
@@ -161,9 +166,28 @@ type faviconDatabaseAdapter struct {
161 wkCtx *WebKitContext166 wkCtx *WebKitContext
162 }167 }
163 168
164-func (*faviconDatabaseAdapter) GetFaviconAsync(_ string, callback func(port.Texture)) {169+func (a *faviconDatabaseAdapter) GetFaviconAsync(pageURL string, callback func(port.Texture)) {
165- // FaviconDatabase async lookup is not yet wired through this adapter.170+ db := a.wkCtx.FaviconDatabase()
166- // The underlying API is: wkCtx.FaviconDatabase().GetFavicon(uri, callback).171+ if db == nil {
167- // This stub satisfies the interface contract; callers will receive nil.172+ callback(nil)
168- callback(nil)173+ return
174+ }
175+
176+ asyncCb := gio.AsyncReadyCallback(func(_ uintptr, resultPtr uintptr, _ uintptr) {
177+ if resultPtr == 0 {
178+ callback(nil)
179+ return
180+ }
181+
182+ result := &gio.AsyncResultBase{Ptr: resultPtr}
183+ texture, err := db.GetFaviconFinish(result)
184+ if err != nil || texture == nil {
185+ callback(nil)
186+ return
187+ }
188+
189+ callback(texture)
190+ })
191+
192+ db.GetFavicon(pageURL, nil, &asyncCb, 0)
169 }193 }
M internal/infrastructure/webkit/engine_init.go +11 -9
@@ -34,7 +34,7 @@ func NewEngine(
3434 engineConfigureRenderingEnvironment(ctx, cfg, wkCfg, &perfSettings, logger)
3535 logging.Trace().Mark("render_env")
3636
37- // --- Build WebKitContextOptions from opts + wkCfg + perfSettings ---
37+ // --- Build webKitContextOptions from opts + wkCfg + perfSettings ---
3838 wkOpts := engineBuildContextOptions(opts, wkCfg, &perfSettings)
3939 logger.Info().
4040 Str("cookie_policy", string(wkOpts.CookiePolicy)).
@@ -104,6 +104,7 @@ func NewEngine(
104104
105105 // --- Assemble Engine ---
106106 engine := &Engine{
107+ ctx: ctx,
107108 wkCtx: wkCtx,
108109 settings: settings,
109110 injector: injector,
@@ -134,10 +135,11 @@ func engineSurveyHardwareAndResolveProfile(
134135 Uint64("vram_mb", hwInfo.VRAM/(1024*1024)).
135136 Msg("hardware survey completed")
136137
137- perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo)
138+ perfCfg := config.PerformanceConfigFromEngine(&cfg.Engine)
139+ perfSettings := config.ResolvePerformanceProfile(&perfCfg, &hwInfo)
138140 logging.Trace().Mark("performance_profile")
139141 logger.Info().
140- Str("profile", string(cfg.Performance.Profile)).
142+ Str("profile", string(cfg.Engine.Profile)).
141143 Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads).
142144 Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads).
143145 Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount).
@@ -194,21 +196,21 @@ func engineConfigureRenderingEnvironment(
194196 Msg("rendering environment configured")
195197 }
196198
197-// engineBuildContextOptions builds port.WebKitContextOptions from EngineOptions, wkCfg and perfSettings.
199+// engineBuildContextOptions builds webKitContextOptions from EngineOptions, wkCfg and perfSettings.
198200 func engineBuildContextOptions(
199201 opts port.EngineOptions,
200202 wkCfg WebKitEngineConfig,
201203 perfSettings *config.ResolvedPerformanceSettings,
202-) port.WebKitContextOptions {
203- cookiePolicy := port.WebKitCookiePolicyNoThirdParty
204+) webKitContextOptions {
205+ cp := cookiePolicyNoThirdParty
204206 if opts.CookiePolicy != "" {
205- cookiePolicy = port.WebKitCookiePolicy(opts.CookiePolicy)
207+ cp = opts.CookiePolicy
206208 }
207209
208- wkOpts := port.WebKitContextOptions{
210+ wkOpts := webKitContextOptions{
209211 DataDir: opts.DataDir,
210212 CacheDir: opts.CacheDir,
211- CookiePolicy: cookiePolicy,
213+ CookiePolicy: cp,
212214 ITPEnabled: wkCfg.ITPEnabled,
213215 }
214216
@@ -34,7 +34,7 @@ func NewEngine(
34 engineConfigureRenderingEnvironment(ctx, cfg, wkCfg, &perfSettings, logger)34 engineConfigureRenderingEnvironment(ctx, cfg, wkCfg, &perfSettings, logger)
35 logging.Trace().Mark("render_env")35 logging.Trace().Mark("render_env")
36 36
37- // --- Build WebKitContextOptions from opts + wkCfg + perfSettings ---37+ // --- Build webKitContextOptions from opts + wkCfg + perfSettings ---
38 wkOpts := engineBuildContextOptions(opts, wkCfg, &perfSettings)38 wkOpts := engineBuildContextOptions(opts, wkCfg, &perfSettings)
39 logger.Info().39 logger.Info().
40 Str("cookie_policy", string(wkOpts.CookiePolicy)).40 Str("cookie_policy", string(wkOpts.CookiePolicy)).
@@ -104,6 +104,7 @@ func NewEngine(
104 104
105 // --- Assemble Engine ---105 // --- Assemble Engine ---
106 engine := &Engine{106 engine := &Engine{
107+ ctx: ctx,
107 wkCtx: wkCtx,108 wkCtx: wkCtx,
108 settings: settings,109 settings: settings,
109 injector: injector,110 injector: injector,
@@ -134,10 +135,11 @@ func engineSurveyHardwareAndResolveProfile(
134 Uint64("vram_mb", hwInfo.VRAM/(1024*1024)).135 Uint64("vram_mb", hwInfo.VRAM/(1024*1024)).
135 Msg("hardware survey completed")136 Msg("hardware survey completed")
136 137
137- perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo)138+ perfCfg := config.PerformanceConfigFromEngine(&cfg.Engine)
139+ perfSettings := config.ResolvePerformanceProfile(&perfCfg, &hwInfo)
138 logging.Trace().Mark("performance_profile")140 logging.Trace().Mark("performance_profile")
139 logger.Info().141 logger.Info().
140- Str("profile", string(cfg.Performance.Profile)).142+ Str("profile", string(cfg.Engine.Profile)).
141 Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads).143 Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads).
142 Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads).144 Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads).
143 Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount).145 Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount).
@@ -194,21 +196,21 @@ func engineConfigureRenderingEnvironment(
194 Msg("rendering environment configured")196 Msg("rendering environment configured")
195 }197 }
196 198
197-// engineBuildContextOptions builds port.WebKitContextOptions from EngineOptions, wkCfg and perfSettings.199+// engineBuildContextOptions builds webKitContextOptions from EngineOptions, wkCfg and perfSettings.
198 func engineBuildContextOptions(200 func engineBuildContextOptions(
199 opts port.EngineOptions,201 opts port.EngineOptions,
200 wkCfg WebKitEngineConfig,202 wkCfg WebKitEngineConfig,
201 perfSettings *config.ResolvedPerformanceSettings,203 perfSettings *config.ResolvedPerformanceSettings,
202-) port.WebKitContextOptions {204+) webKitContextOptions {
203- cookiePolicy := port.WebKitCookiePolicyNoThirdParty205+ cp := cookiePolicyNoThirdParty
204 if opts.CookiePolicy != "" {206 if opts.CookiePolicy != "" {
205- cookiePolicy = port.WebKitCookiePolicy(opts.CookiePolicy)207+ cp = opts.CookiePolicy
206 }208 }
207 209
208- wkOpts := port.WebKitContextOptions{210+ wkOpts := webKitContextOptions{
209 DataDir: opts.DataDir,211 DataDir: opts.DataDir,
210 CacheDir: opts.CacheDir,212 CacheDir: opts.CacheDir,
211- CookiePolicy: cookiePolicy,213+ CookiePolicy: cp,
212 ITPEnabled: wkCfg.ITPEnabled,214 ITPEnabled: wkCfg.ITPEnabled,
213 }215 }
214 216
M internal/infrastructure/webkit/factory.go +1 -1
@@ -103,7 +103,7 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
103103
104104 // Set background color to match theme (eliminates white flash)
105105 if r, g, b, a := f.bg.get(); a > 0 {
106- wv.SetBackgroundColor(r, g, b, a)
106+ wv.SetBackgroundColor(float64(r), float64(g), float64(b), float64(a))
107107 }
108108
109109 // Keep hidden until content is painted
@@ -103,7 +103,7 @@ func (f *WebViewFactory) CreateRelated(ctx context.Context, parentID port.WebVie
103 103
104 // Set background color to match theme (eliminates white flash)104 // Set background color to match theme (eliminates white flash)
105 if r, g, b, a := f.bg.get(); a > 0 {105 if r, g, b, a := f.bg.get(); a > 0 {
106- wv.SetBackgroundColor(r, g, b, a)106+ wv.SetBackgroundColor(float64(r), float64(g), float64(b), float64(a))
107 }107 }
108 108
109 // Keep hidden until content is painted109 // Keep hidden until content is painted
M internal/infrastructure/webkit/injector.go +25 -0
@@ -413,3 +413,28 @@ func (ci *ContentInjector) InjectScripts(ctx context.Context, ucm *webkit.UserCo
413413
414414 log.Debug().Bool("prefers_dark", prefersDark).Bool("auto_copy", autoCopyEnabled).Msg("scripts injected")
415415 }
416+
417+// RefreshScripts clears and re-injects user scripts for a single WebView.
418+// Implements port.ContentInjector.
419+// Returns nil for "not applicable" cases (nil wv, wrong engine type, nil UCM).
420+func (ci *ContentInjector) RefreshScripts(ctx context.Context, wv port.WebView) error {
421+ log := logging.FromContext(ctx).With().Str("component", "content-injector").Logger()
422+ if wv == nil {
423+ log.Debug().Msg("RefreshScripts: nil webview")
424+ return nil
425+ }
426+ wkWV, ok := wv.(*WebView)
427+ if !ok {
428+ log.Debug().Msg("RefreshScripts: webview is not *webkit.WebView")
429+ return nil
430+ }
431+ ucm := wkWV.UserContentManager()
432+ if ucm == nil {
433+ log.Debug().Uint64("webview_id", uint64(wv.ID())).Msg("RefreshScripts: nil UserContentManager")
434+ return nil
435+ }
436+ ucm.RemoveAllScripts()
437+ ucm.RemoveAllStyleSheets()
438+ ci.InjectScripts(ctx, ucm, wv.ID())
439+ return nil
440+}
@@ -413,3 +413,28 @@ func (ci *ContentInjector) InjectScripts(ctx context.Context, ucm *webkit.UserCo
413 413
414 log.Debug().Bool("prefers_dark", prefersDark).Bool("auto_copy", autoCopyEnabled).Msg("scripts injected")414 log.Debug().Bool("prefers_dark", prefersDark).Bool("auto_copy", autoCopyEnabled).Msg("scripts injected")
415 }415 }
416+
417+// RefreshScripts clears and re-injects user scripts for a single WebView.
418+// Implements port.ContentInjector.
419+// Returns nil for "not applicable" cases (nil wv, wrong engine type, nil UCM).
420+func (ci *ContentInjector) RefreshScripts(ctx context.Context, wv port.WebView) error {
421+ log := logging.FromContext(ctx).With().Str("component", "content-injector").Logger()
422+ if wv == nil {
423+ log.Debug().Msg("RefreshScripts: nil webview")
424+ return nil
425+ }
426+ wkWV, ok := wv.(*WebView)
427+ if !ok {
428+ log.Debug().Msg("RefreshScripts: webview is not *webkit.WebView")
429+ return nil
430+ }
431+ ucm := wkWV.UserContentManager()
432+ if ucm == nil {
433+ log.Debug().Uint64("webview_id", uint64(wv.ID())).Msg("RefreshScripts: nil UserContentManager")
434+ return nil
435+ }
436+ ucm.RemoveAllScripts()
437+ ucm.RemoveAllStyleSheets()
438+ ci.InjectScripts(ctx, ucm, wv.ID())
439+ return nil
440+}
M internal/infrastructure/webkit/messaging.go +2 -2
@@ -276,7 +276,7 @@ func (r *MessageRouter) syncWebViewID(wv *WebView) {
276276 return
277277 }
278278 script := fmt.Sprintf("window.__dumber_webview_id=%d;", uint64(wv.ID()))
279- wv.RunJavaScript(r.baseCtx, script, "")
279+ wv.RunJavaScriptInWorld(r.baseCtx, script, "")
280280 }
281281
282282 func (r *MessageRouter) markWebViewIDSynced(id WebViewID) bool {
@@ -329,6 +329,6 @@ func (r *MessageRouter) dispatchResponse(ctx context.Context, webviewID WebViewI
329329 string(data),
330330 )
331331
332- wv.RunJavaScript(ctx, script, world)
332+ wv.RunJavaScriptInWorld(ctx, script, world)
333333 return nil
334334 }
@@ -276,7 +276,7 @@ func (r *MessageRouter) syncWebViewID(wv *WebView) {
276 return276 return
277 }277 }
278 script := fmt.Sprintf("window.__dumber_webview_id=%d;", uint64(wv.ID()))278 script := fmt.Sprintf("window.__dumber_webview_id=%d;", uint64(wv.ID()))
279- wv.RunJavaScript(r.baseCtx, script, "")279+ wv.RunJavaScriptInWorld(r.baseCtx, script, "")
280 }280 }
281 281
282 func (r *MessageRouter) markWebViewIDSynced(id WebViewID) bool {282 func (r *MessageRouter) markWebViewIDSynced(id WebViewID) bool {
@@ -329,6 +329,6 @@ func (r *MessageRouter) dispatchResponse(ctx context.Context, webviewID WebViewI
329 string(data),329 string(data),
330 )330 )
331 331
332- wv.RunJavaScript(ctx, script, world)332+ wv.RunJavaScriptInWorld(ctx, script, world)
333 return nil333 return nil
334 }334 }
M internal/infrastructure/webkit/pool.go +1 -1
@@ -159,7 +159,7 @@ func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
159159 case wv := <-p.pool:
160160 if wv != nil && !wv.IsDestroyed() {
161161 if r, g, b, a := p.bg.get(); a > 0 {
162- wv.SetBackgroundColor(r, g, b, a)
162+ wv.SetBackgroundColor(float64(r), float64(g), float64(b), float64(a))
163163 }
164164 wv.inner.AddCssClass("webview-themed")
165165 // Keep pooled WebViews hidden until we explicitly reveal them.
@@ -159,7 +159,7 @@ func (p *WebViewPool) Acquire(ctx context.Context) (*WebView, error) {
159 case wv := <-p.pool:159 case wv := <-p.pool:
160 if wv != nil && !wv.IsDestroyed() {160 if wv != nil && !wv.IsDestroyed() {
161 if r, g, b, a := p.bg.get(); a > 0 {161 if r, g, b, a := p.bg.get(); a > 0 {
162- wv.SetBackgroundColor(r, g, b, a)162+ wv.SetBackgroundColor(float64(r), float64(g), float64(b), float64(a))
163 }163 }
164 wv.inner.AddCssClass("webview-themed")164 wv.inner.AddCssClass("webview-themed")
165 // Keep pooled WebViews hidden until we explicitly reveal them.165 // Keep pooled WebViews hidden until we explicitly reveal them.
M internal/infrastructure/webkit/scheme_handler.go +8 -7
@@ -347,7 +347,8 @@ func (h *DumbSchemeHandler) buildConfigResponse(cfg *config.Config) *SchemeRespo
347347 }
348348
349349 // Resolve performance profile with hardware info
350- resolved := config.ResolvePerformanceProfile(&cfg.Performance, hw)
350+ perfCfg := config.PerformanceConfigFromEngine(&cfg.Engine)
351+ resolved := config.ResolvePerformanceProfile(&perfCfg, hw)
351352
352353 resp := configPayload{
353354 DefaultUIScale: cfg.DefaultUIScale,
@@ -363,13 +364,13 @@ func (h *DumbSchemeHandler) buildConfigResponse(cfg *config.Config) *SchemeRespo
363364 DarkPalette: cfg.Appearance.DarkPalette,
364365 },
365366 Performance: configPerformancePayload{
366- Profile: string(cfg.Performance.Profile),
367+ Profile: string(cfg.Engine.Profile),
367368 Custom: configCustomPerformance{
368- SkiaCPUThreads: cfg.Performance.SkiaCPUPaintingThreads,
369- SkiaGPUThreads: cfg.Performance.SkiaGPUPaintingThreads,
370- WebProcessMemoryMB: cfg.Performance.WebProcessMemoryLimitMB,
371- NetworkProcessMemoryMB: cfg.Performance.NetworkProcessMemoryLimitMB,
372- WebViewPoolPrewarm: cfg.Performance.WebViewPoolPrewarmCount,
369+ SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,
370+ SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,
371+ WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,
372+ NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,
373+ WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,
373374 },
374375 Resolved: configResolvedPerformance{
375376 SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,
@@ -347,7 +347,8 @@ func (h *DumbSchemeHandler) buildConfigResponse(cfg *config.Config) *SchemeRespo
347 }347 }
348 348
349 // Resolve performance profile with hardware info349 // Resolve performance profile with hardware info
350- resolved := config.ResolvePerformanceProfile(&cfg.Performance, hw)350+ perfCfg := config.PerformanceConfigFromEngine(&cfg.Engine)
351+ resolved := config.ResolvePerformanceProfile(&perfCfg, hw)
351 352
352 resp := configPayload{353 resp := configPayload{
353 DefaultUIScale: cfg.DefaultUIScale,354 DefaultUIScale: cfg.DefaultUIScale,
@@ -363,13 +364,13 @@ func (h *DumbSchemeHandler) buildConfigResponse(cfg *config.Config) *SchemeRespo
363 DarkPalette: cfg.Appearance.DarkPalette,364 DarkPalette: cfg.Appearance.DarkPalette,
364 },365 },
365 Performance: configPerformancePayload{366 Performance: configPerformancePayload{
366- Profile: string(cfg.Performance.Profile),367+ Profile: string(cfg.Engine.Profile),
367 Custom: configCustomPerformance{368 Custom: configCustomPerformance{
368- SkiaCPUThreads: cfg.Performance.SkiaCPUPaintingThreads,369+ SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,
369- SkiaGPUThreads: cfg.Performance.SkiaGPUPaintingThreads,370+ SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,
370- WebProcessMemoryMB: cfg.Performance.WebProcessMemoryLimitMB,371+ WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,
371- NetworkProcessMemoryMB: cfg.Performance.NetworkProcessMemoryLimitMB,372+ NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,
372- WebViewPoolPrewarm: cfg.Performance.WebViewPoolPrewarmCount,373+ WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,
373 },374 },
374 Resolved: configResolvedPerformance{375 Resolved: configResolvedPerformance{
375 SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,376 SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,
M internal/infrastructure/webkit/settings.go +4 -11
@@ -65,7 +65,6 @@ func (sm *SettingsManager) applySettings(ctx context.Context, settings *webkit.S
6565
6666 log.Debug().
6767 Str("sans_font", cfg.Appearance.SansFont).
68- Str("rendering_mode", string(cfg.Rendering.Mode)).
6968 Bool("developer_extras", cfg.Debug.EnableDevTools).
7069 Bool("webrtc_enabled", webrtcEnabled).
7170 Bool("media_stream_enabled", mediaStreamEnabled).
@@ -93,21 +92,15 @@ func applyFontSettings(settings *webkit.Settings, cfg *config.Config) {
9392 }
9493 }
9594
96-func applyRenderingSettings(settings *webkit.Settings, cfg *config.Config) {
97- switch cfg.Rendering.Mode {
98- case config.RenderingModeGPU:
99- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
100- case config.RenderingModeCPU:
101- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)
102- case config.RenderingModeAuto:
103- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
104- }
95+func applyRenderingSettings(_ *webkit.Settings, _ *config.Config) {
96+ // Hardware acceleration policy is now managed by the engine layer via
97+ // cfg.Engine.WebKit. No WebKit settings to apply here at the settings layer.
10598 }
10699
107100 func applyDebugSettings(settings *webkit.Settings, cfg *config.Config) {
108101 settings.SetEnableDeveloperExtras(cfg.Debug.EnableDevTools)
109102 settings.SetEnableWriteConsoleMessagesToStdout(cfg.Logging.CaptureConsole)
110- settings.SetDrawCompositingIndicators(cfg.Rendering.DrawCompositingIndicators)
103+ settings.SetDrawCompositingIndicators(cfg.Engine.WebKit.DrawCompositingIndicators)
111104 }
112105
113106 func applyBrowsingSettings(settings *webkit.Settings) {
@@ -65,7 +65,6 @@ func (sm *SettingsManager) applySettings(ctx context.Context, settings *webkit.S
65 65
66 log.Debug().66 log.Debug().
67 Str("sans_font", cfg.Appearance.SansFont).67 Str("sans_font", cfg.Appearance.SansFont).
68- Str("rendering_mode", string(cfg.Rendering.Mode)).
69 Bool("developer_extras", cfg.Debug.EnableDevTools).68 Bool("developer_extras", cfg.Debug.EnableDevTools).
70 Bool("webrtc_enabled", webrtcEnabled).69 Bool("webrtc_enabled", webrtcEnabled).
71 Bool("media_stream_enabled", mediaStreamEnabled).70 Bool("media_stream_enabled", mediaStreamEnabled).
@@ -93,21 +92,15 @@ func applyFontSettings(settings *webkit.Settings, cfg *config.Config) {
93 }92 }
94 }93 }
95 94
96-func applyRenderingSettings(settings *webkit.Settings, cfg *config.Config) {95+func applyRenderingSettings(_ *webkit.Settings, _ *config.Config) {
97- switch cfg.Rendering.Mode {96+ // Hardware acceleration policy is now managed by the engine layer via
98- case config.RenderingModeGPU:97+ // cfg.Engine.WebKit. No WebKit settings to apply here at the settings layer.
99- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
100- case config.RenderingModeCPU:
101- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)
102- case config.RenderingModeAuto:
103- settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
104- }
105 }98 }
106 99
107 func applyDebugSettings(settings *webkit.Settings, cfg *config.Config) {100 func applyDebugSettings(settings *webkit.Settings, cfg *config.Config) {
108 settings.SetEnableDeveloperExtras(cfg.Debug.EnableDevTools)101 settings.SetEnableDeveloperExtras(cfg.Debug.EnableDevTools)
109 settings.SetEnableWriteConsoleMessagesToStdout(cfg.Logging.CaptureConsole)102 settings.SetEnableWriteConsoleMessagesToStdout(cfg.Logging.CaptureConsole)
110- settings.SetDrawCompositingIndicators(cfg.Rendering.DrawCompositingIndicators)103+ settings.SetDrawCompositingIndicators(cfg.Engine.WebKit.DrawCompositingIndicators)
111 }104 }
112 105
113 func applyBrowsingSettings(settings *webkit.Settings) {106 func applyBrowsingSettings(settings *webkit.Settings) {
M internal/infrastructure/webkit/webview.go +46 -10
@@ -1400,9 +1400,22 @@ func (wv *WebView) Title() string {
14001400 return wv.title
14011401 }
14021402
1403-// Favicon returns the current page favicon as a GdkTexture.
1403+// Favicon returns the current page favicon as a Texture.
14041404 // Returns nil if no favicon is available.
1405-func (wv *WebView) Favicon() *gdk.Texture {
1405+func (wv *WebView) Favicon() port.Texture {
1406+ if wv.destroyed.Load() {
1407+ return nil
1408+ }
1409+ tex := wv.inner.GetFavicon()
1410+ if tex == nil {
1411+ return nil
1412+ }
1413+ return tex
1414+}
1415+
1416+// FaviconGdk returns the current page favicon as a *gdk.Texture.
1417+// Used by internal WebKit code that needs the concrete type.
1418+func (wv *WebView) FaviconGdk() *gdk.Texture {
14061419 if wv.destroyed.Load() {
14071420 return nil
14081421 }
@@ -1447,18 +1460,18 @@ func (wv *WebView) GetZoomLevel() float64 {
14471460 return wv.inner.GetZoomLevel()
14481461 }
14491462
1450-// SetBackgroundColor sets the WebView background color.
1463+// SetBackgroundColor sets the WebView background color (port.WebView interface).
14511464 // This color is shown before content is painted, eliminating white flash.
14521465 // Values are in range 0.0-1.0 for red, green, blue, alpha.
1453-func (wv *WebView) SetBackgroundColor(r, g, b, a float32) {
1466+func (wv *WebView) SetBackgroundColor(r, g, b, a float64) {
14541467 if wv.destroyed.Load() {
14551468 return
14561469 }
14571470 rgba := &gdk.RGBA{
1458- Red: r,
1459- Green: g,
1460- Blue: b,
1461- Alpha: a,
1471+ Red: float32(r),
1472+ Green: float32(g),
1473+ Blue: float32(b),
1474+ Alpha: float32(a),
14621475 }
14631476 wv.inner.SetBackgroundColor(rgba)
14641477 }
@@ -1469,6 +1482,19 @@ func (wv *WebView) ResetBackgroundToDefault() {
14691482 wv.SetBackgroundColor(1.0, 1.0, 1.0, 1.0)
14701483 }
14711484
1485+// NativeWidget returns the underlying GTK widget pointer for UI embedding.
1486+// Implements port.NativeWidgetProvider.
1487+func (wv *WebView) NativeWidget() uintptr {
1488+ if wv.destroyed.Load() {
1489+ return 0
1490+ }
1491+ w := wv.Widget()
1492+ if w == nil {
1493+ return 0
1494+ }
1495+ return w.Widget.GoPointer()
1496+}
1497+
14721498 // Show makes the WebView widget visible.
14731499 // This should be called after the WebView is ready to be displayed.
14741500 func (wv *WebView) Show() {
@@ -1576,6 +1602,10 @@ func (wv *WebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
15761602 wv.OnWebProcessTerminated = nil
15771603 }
15781604 wv.OnPermissionRequest = callbacks.OnPermissionRequest
1605+ wv.OnLinkMiddleClick = callbacks.OnLinkMiddleClick
1606+ wv.OnEnterFullscreen = callbacks.OnEnterFullscreen
1607+ wv.OnLeaveFullscreen = callbacks.OnLeaveFullscreen
1608+ wv.OnAudioStateChanged = callbacks.OnAudioStateChanged
15791609 }
15801610
15811611 // ShowDevTools opens the WebKit inspector/developer tools.
@@ -1969,10 +1999,16 @@ func (wv *WebView) shouldLogRunJSError(domain, signature string, nonFatal bool,
19691999 return shouldLog, count
19702000 }
19712001
1972-// RunJavaScript executes script in the specified world (empty for main world).
2002+// RunJavaScript executes script in the main world (port.WebView interface).
2003+// This is fire-and-forget: it does not block and errors are logged asynchronously.
2004+func (wv *WebView) RunJavaScript(ctx context.Context, script string) {
2005+ wv.RunJavaScriptInWorld(ctx, script, "")
2006+}
2007+
2008+// RunJavaScriptInWorld executes script in the specified world (empty for main world).
19732009 // This is fire-and-forget: it does not block and errors are logged asynchronously.
19742010 // Safe to call from any context including GTK signal handlers.
1975-func (wv *WebView) RunJavaScript(ctx context.Context, script, worldName string) {
2011+func (wv *WebView) RunJavaScriptInWorld(ctx context.Context, script, worldName string) {
19762012 if wv.destroyed.Load() {
19772013 return
19782014 }
@@ -1400,9 +1400,22 @@ func (wv *WebView) Title() string {
1400 return wv.title1400 return wv.title
1401 }1401 }
1402 1402
1403-// Favicon returns the current page favicon as a GdkTexture.1403+// Favicon returns the current page favicon as a Texture.
1404 // Returns nil if no favicon is available.1404 // Returns nil if no favicon is available.
1405-func (wv *WebView) Favicon() *gdk.Texture {1405+func (wv *WebView) Favicon() port.Texture {
1406+ if wv.destroyed.Load() {
1407+ return nil
1408+ }
1409+ tex := wv.inner.GetFavicon()
1410+ if tex == nil {
1411+ return nil
1412+ }
1413+ return tex
1414+}
1415+
1416+// FaviconGdk returns the current page favicon as a *gdk.Texture.
1417+// Used by internal WebKit code that needs the concrete type.
1418+func (wv *WebView) FaviconGdk() *gdk.Texture {
1406 if wv.destroyed.Load() {1419 if wv.destroyed.Load() {
1407 return nil1420 return nil
1408 }1421 }
@@ -1447,18 +1460,18 @@ func (wv *WebView) GetZoomLevel() float64 {
1447 return wv.inner.GetZoomLevel()1460 return wv.inner.GetZoomLevel()
1448 }1461 }
1449 1462
1450-// SetBackgroundColor sets the WebView background color.1463+// SetBackgroundColor sets the WebView background color (port.WebView interface).
1451 // This color is shown before content is painted, eliminating white flash.1464 // This color is shown before content is painted, eliminating white flash.
1452 // Values are in range 0.0-1.0 for red, green, blue, alpha.1465 // Values are in range 0.0-1.0 for red, green, blue, alpha.
1453-func (wv *WebView) SetBackgroundColor(r, g, b, a float32) {1466+func (wv *WebView) SetBackgroundColor(r, g, b, a float64) {
1454 if wv.destroyed.Load() {1467 if wv.destroyed.Load() {
1455 return1468 return
1456 }1469 }
1457 rgba := &gdk.RGBA{1470 rgba := &gdk.RGBA{
1458- Red: r,1471+ Red: float32(r),
1459- Green: g,1472+ Green: float32(g),
1460- Blue: b,1473+ Blue: float32(b),
1461- Alpha: a,1474+ Alpha: float32(a),
1462 }1475 }
1463 wv.inner.SetBackgroundColor(rgba)1476 wv.inner.SetBackgroundColor(rgba)
1464 }1477 }
@@ -1469,6 +1482,19 @@ func (wv *WebView) ResetBackgroundToDefault() {
1469 wv.SetBackgroundColor(1.0, 1.0, 1.0, 1.0)1482 wv.SetBackgroundColor(1.0, 1.0, 1.0, 1.0)
1470 }1483 }
1471 1484
1485+// NativeWidget returns the underlying GTK widget pointer for UI embedding.
1486+// Implements port.NativeWidgetProvider.
1487+func (wv *WebView) NativeWidget() uintptr {
1488+ if wv.destroyed.Load() {
1489+ return 0
1490+ }
1491+ w := wv.Widget()
1492+ if w == nil {
1493+ return 0
1494+ }
1495+ return w.Widget.GoPointer()
1496+}
1497+
1472 // Show makes the WebView widget visible.1498 // Show makes the WebView widget visible.
1473 // This should be called after the WebView is ready to be displayed.1499 // This should be called after the WebView is ready to be displayed.
1474 func (wv *WebView) Show() {1500 func (wv *WebView) Show() {
@@ -1576,6 +1602,10 @@ func (wv *WebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
1576 wv.OnWebProcessTerminated = nil1602 wv.OnWebProcessTerminated = nil
1577 }1603 }
1578 wv.OnPermissionRequest = callbacks.OnPermissionRequest1604 wv.OnPermissionRequest = callbacks.OnPermissionRequest
1605+ wv.OnLinkMiddleClick = callbacks.OnLinkMiddleClick
1606+ wv.OnEnterFullscreen = callbacks.OnEnterFullscreen
1607+ wv.OnLeaveFullscreen = callbacks.OnLeaveFullscreen
1608+ wv.OnAudioStateChanged = callbacks.OnAudioStateChanged
1579 }1609 }
1580 1610
1581 // ShowDevTools opens the WebKit inspector/developer tools.1611 // ShowDevTools opens the WebKit inspector/developer tools.
@@ -1969,10 +1999,16 @@ func (wv *WebView) shouldLogRunJSError(domain, signature string, nonFatal bool,
1969 return shouldLog, count1999 return shouldLog, count
1970 }2000 }
1971 2001
1972-// RunJavaScript executes script in the specified world (empty for main world).2002+// RunJavaScript executes script in the main world (port.WebView interface).
2003+// This is fire-and-forget: it does not block and errors are logged asynchronously.
2004+func (wv *WebView) RunJavaScript(ctx context.Context, script string) {
2005+ wv.RunJavaScriptInWorld(ctx, script, "")
2006+}
2007+
2008+// RunJavaScriptInWorld executes script in the specified world (empty for main world).
1973 // This is fire-and-forget: it does not block and errors are logged asynchronously.2009 // This is fire-and-forget: it does not block and errors are logged asynchronously.
1974 // Safe to call from any context including GTK signal handlers.2010 // Safe to call from any context including GTK signal handlers.
1975-func (wv *WebView) RunJavaScript(ctx context.Context, script, worldName string) {2011+func (wv *WebView) RunJavaScriptInWorld(ctx context.Context, script, worldName string) {
1976 if wv.destroyed.Load() {2012 if wv.destroyed.Load() {
1977 return2013 return
1978 }2014 }
M internal/ui/adapter/favicon.go +14 -23
@@ -5,12 +5,11 @@ import (
55 "context"
66 "sync"
77
8+ "github.com/bnema/dumber/internal/application/port"
89 domainurl "github.com/bnema/dumber/internal/domain/url"
910 "github.com/bnema/dumber/internal/infrastructure/favicon"
10- "github.com/bnema/dumber/internal/infrastructure/webkit"
1111 "github.com/bnema/dumber/internal/logging"
1212 "github.com/jwijenbergh/puregotk/v4/gdk"
13- "github.com/jwijenbergh/puregotk/v4/gio"
1413 "github.com/jwijenbergh/puregotk/v4/glib"
1514 "github.com/rs/zerolog"
1615 )
@@ -19,7 +18,7 @@ import (
1918 // gdk.Texture conversion and WebKit FaviconDatabase integration.
2019 type FaviconAdapter struct {
2120 service *favicon.Service
22- faviconDB *webkit.FaviconDatabase
21+ faviconDB port.FaviconDatabase
2322 textureCache map[string]*gdk.Texture
2423 mu sync.RWMutex
2524 warnMu sync.Mutex
@@ -29,8 +28,8 @@ type FaviconAdapter struct {
2928 type warningLogFunc func(log *zerolog.Logger, err error)
3029
3130 // NewFaviconAdapter creates a new FaviconAdapter.
32-// The faviconDB can be nil if WebKit favicon database is not available.
33-func NewFaviconAdapter(service *favicon.Service, faviconDB *webkit.FaviconDatabase) *FaviconAdapter {
31+// The faviconDB can be nil if a favicon database is not available.
32+func NewFaviconAdapter(service *favicon.Service, faviconDB port.FaviconDatabase) *FaviconAdapter {
3433 return &FaviconAdapter{
3534 service: service,
3635 faviconDB: faviconDB,
@@ -127,32 +126,24 @@ func (a *FaviconAdapter) GetOrFetch(ctx context.Context, pageURL string, callbac
127126 }()
128127 }
129128
130- // Try WebKit FaviconDatabase if available
129+ // Try engine FaviconDatabase if available
131130 if a.faviconDB != nil {
132- asyncCb := gio.AsyncReadyCallback(func(_ uintptr, resultPtr uintptr, _ uintptr) {
133- if resultPtr == 0 {
134- log.Debug().Str("domain", domain).Msg("favicon not in webkit db, fetching via service")
131+ a.faviconDB.GetFaviconAsync(pageURL, func(tex port.Texture) {
132+ if tex == nil {
133+ log.Debug().Str("domain", domain).Msg("favicon not in engine db, fetching via service")
135134 fetchViaService()
136135 return
137136 }
138-
139- result := &gio.AsyncResultBase{Ptr: resultPtr}
140- texture, err := a.faviconDB.GetFaviconFinish(result)
141- if err != nil || texture == nil {
142- log.Debug().Str("domain", domain).Msg("favicon not in webkit db, fetching via service")
137+ // Convert port.Texture to *gdk.Texture
138+ gdkTex, ok := tex.(*gdk.Texture)
139+ if !ok {
143140 fetchViaService()
144141 return
145142 }
146-
147- // Found in WebKit database
148- a.setTexture(domain, texture)
149- invokeCallback(texture)
150-
151- // Ensure disk cache is populated (async)
152- go a.service.EnsureDiskCache(ctx, domain)
143+ a.setTexture(domain, gdkTex)
144+ invokeCallback(gdkTex)
145+ a.saveFaviconToDisk(ctx, domain, gdkTex)
153146 })
154-
155- a.faviconDB.GetFavicon(pageURL, nil, &asyncCb, 0)
156147 return
157148 }
158149
@@ -5,12 +5,11 @@ import (
5 "context"5 "context"
6 "sync"6 "sync"
7 7
8+ "github.com/bnema/dumber/internal/application/port"
8 domainurl "github.com/bnema/dumber/internal/domain/url"9 domainurl "github.com/bnema/dumber/internal/domain/url"
9 "github.com/bnema/dumber/internal/infrastructure/favicon"10 "github.com/bnema/dumber/internal/infrastructure/favicon"
10- "github.com/bnema/dumber/internal/infrastructure/webkit"
11 "github.com/bnema/dumber/internal/logging"11 "github.com/bnema/dumber/internal/logging"
12 "github.com/jwijenbergh/puregotk/v4/gdk"12 "github.com/jwijenbergh/puregotk/v4/gdk"
13- "github.com/jwijenbergh/puregotk/v4/gio"
14 "github.com/jwijenbergh/puregotk/v4/glib"13 "github.com/jwijenbergh/puregotk/v4/glib"
15 "github.com/rs/zerolog"14 "github.com/rs/zerolog"
16 )15 )
@@ -19,7 +18,7 @@ import (
19 // gdk.Texture conversion and WebKit FaviconDatabase integration.18 // gdk.Texture conversion and WebKit FaviconDatabase integration.
20 type FaviconAdapter struct {19 type FaviconAdapter struct {
21 service *favicon.Service20 service *favicon.Service
22- faviconDB *webkit.FaviconDatabase21+ faviconDB port.FaviconDatabase
23 textureCache map[string]*gdk.Texture22 textureCache map[string]*gdk.Texture
24 mu sync.RWMutex23 mu sync.RWMutex
25 warnMu sync.Mutex24 warnMu sync.Mutex
@@ -29,8 +28,8 @@ type FaviconAdapter struct {
29 type warningLogFunc func(log *zerolog.Logger, err error)28 type warningLogFunc func(log *zerolog.Logger, err error)
30 29
31 // NewFaviconAdapter creates a new FaviconAdapter.30 // NewFaviconAdapter creates a new FaviconAdapter.
32-// The faviconDB can be nil if WebKit favicon database is not available.31+// The faviconDB can be nil if a favicon database is not available.
33-func NewFaviconAdapter(service *favicon.Service, faviconDB *webkit.FaviconDatabase) *FaviconAdapter {32+func NewFaviconAdapter(service *favicon.Service, faviconDB port.FaviconDatabase) *FaviconAdapter {
34 return &FaviconAdapter{33 return &FaviconAdapter{
35 service: service,34 service: service,
36 faviconDB: faviconDB,35 faviconDB: faviconDB,
@@ -127,32 +126,24 @@ func (a *FaviconAdapter) GetOrFetch(ctx context.Context, pageURL string, callbac
127 }()126 }()
128 }127 }
129 128
130- // Try WebKit FaviconDatabase if available129+ // Try engine FaviconDatabase if available
131 if a.faviconDB != nil {130 if a.faviconDB != nil {
132- asyncCb := gio.AsyncReadyCallback(func(_ uintptr, resultPtr uintptr, _ uintptr) {131+ a.faviconDB.GetFaviconAsync(pageURL, func(tex port.Texture) {
133- if resultPtr == 0 {132+ if tex == nil {
134- log.Debug().Str("domain", domain).Msg("favicon not in webkit db, fetching via service")133+ log.Debug().Str("domain", domain).Msg("favicon not in engine db, fetching via service")
135 fetchViaService()134 fetchViaService()
136 return135 return
137 }136 }
138-137+ // Convert port.Texture to *gdk.Texture
139- result := &gio.AsyncResultBase{Ptr: resultPtr}138+ gdkTex, ok := tex.(*gdk.Texture)
140- texture, err := a.faviconDB.GetFaviconFinish(result)139+ if !ok {
141- if err != nil || texture == nil {
142- log.Debug().Str("domain", domain).Msg("favicon not in webkit db, fetching via service")
143 fetchViaService()140 fetchViaService()
144 return141 return
145 }142 }
146-143+ a.setTexture(domain, gdkTex)
147- // Found in WebKit database144+ invokeCallback(gdkTex)
148- a.setTexture(domain, texture)145+ a.saveFaviconToDisk(ctx, domain, gdkTex)
149- invokeCallback(texture)
150-
151- // Ensure disk cache is populated (async)
152- go a.service.EnsureDiskCache(ctx, domain)
153 })146 })
154-
155- a.faviconDB.GetFavicon(pageURL, nil, &asyncCb, 0)
156 return147 return
157 }148 }
158 149
M internal/ui/app.go +65 -51
@@ -102,14 +102,9 @@ type App struct {
102102
103103 // Engine (port-level and concrete for webkit-specific operations)
104104 engine port.Engine
105- wkEngine *webkit.Engine
105+ wkEngine *webkit.Engine // single webkit escape hatch; all webkit-specific ops go through this
106106
107107 // Web content (managed by content.Coordinator)
108- pool *webkit.WebViewPool
109- webViewFactory *webkit.WebViewFactory
110- injector *webkit.ContentInjector
111- router *webkit.MessageRouter
112- settings *webkit.SettingsManager
113108 faviconAdapter *adapter.FaviconAdapter
114109
115110 // App-level toaster for system notifications (filter status, etc.)
@@ -191,18 +186,20 @@ func New(deps *Dependencies) (*App, error) {
191186 // Type-assert engine to concrete *webkit.Engine for webkit-specific operations.
192187 if wke, ok := deps.Engine.(*webkit.Engine); ok {
193188 app.wkEngine = wke
194- app.pool = wke.InternalPool()
195- app.injector = wke.InternalInjector()
196- app.router = wke.InternalMessageRouter()
197- app.settings = wke.InternalSettings()
198189 }
199- if app.router == nil {
200- app.router = webkit.NewMessageRouter(ctx)
190+
191+ // Resolve the message router via wkEngine, with a standalone fallback.
192+ var router *webkit.MessageRouter
193+ if app.wkEngine != nil {
194+ router = app.wkEngine.InternalMessageRouter()
195+ }
196+ if router == nil {
197+ router = webkit.NewMessageRouter(ctx)
201198 }
202199
203200 // Register message handlers
204- if app.router != nil {
205- if err := handlers.RegisterAll(ctx, app.router, handlers.Config{
201+ if router != nil {
202+ if err := handlers.RegisterAll(ctx, router, handlers.Config{
206203 HistoryUC: deps.HistoryUC,
207204 FavoritesUC: deps.FavoritesUC,
208205 Clipboard: deps.Clipboard,
@@ -249,8 +246,10 @@ func (a *App) Run(ctx context.Context, args []string) int {
249246
250247 // Refresh scripts in pooled WebViews that were prewarmed before adw.Init().
251248 // They have the wrong dark mode preference injected; re-inject with correct value.
252- if a.pool != nil {
253- a.pool.RefreshScripts(ctx)
249+ if a.wkEngine != nil {
250+ if pool := a.wkEngine.InternalPool(); pool != nil {
251+ pool.RefreshScripts(ctx)
252+ }
254253 }
255254 }
256255
@@ -375,7 +374,12 @@ func (a *App) setupPoolBackgroundColor(ctx context.Context) {
375374
376375 // Set theme background color on pool to eliminate white flash.
377376 // Must be done before any WebView creation so WebViews get the correct color.
378- if a.pool == nil {
377+ if a.wkEngine == nil {
378+ log.Debug().Msg("webview pool not available; skipping background color setup")
379+ return
380+ }
381+ pool := a.wkEngine.InternalPool()
382+ if pool == nil {
379383 log.Debug().Msg("webview pool not available; skipping background color setup")
380384 return
381385 }
@@ -385,16 +389,20 @@ func (a *App) setupPoolBackgroundColor(ctx context.Context) {
385389 }
386390
387391 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()
388- a.pool.SetBackgroundColor(r, g, b, alpha)
392+ pool.SetBackgroundColor(r, g, b, alpha)
389393 log.Debug().Msg("configured webview pool background color")
390394 }
391395
392396 func (a *App) prewarmWebViewPoolAsync(ctx context.Context) {
393397 // Prewarm WebView pool after startup so cold-start navigation is not blocked.
394- if a.pool == nil {
398+ if a.wkEngine == nil {
395399 return
396400 }
397- a.pool.PrewarmAsync(ctx, 0)
401+ pool := a.wkEngine.InternalPool()
402+ if pool == nil {
403+ return
404+ }
405+ pool.PrewarmAsync(ctx, 0)
398406 }
399407
400408 func (a *App) createMainWindow(ctx context.Context) error {
@@ -558,12 +566,20 @@ func (a *App) initAccentPicker(ctx context.Context) {
558566 func (a *App) registerAccentHandlers(ctx context.Context) {
559567 log := logging.FromContext(ctx)
560568
561- if a.router == nil || a.insertAccentUC == nil {
562- log.Debug().Msg("router or insertAccentUC not available, skipping accent handler registration")
569+ if a.insertAccentUC == nil {
570+ log.Debug().Msg("insertAccentUC not available, skipping accent handler registration")
571+ return
572+ }
573+ var router *webkit.MessageRouter
574+ if a.wkEngine != nil {
575+ router = a.wkEngine.InternalMessageRouter()
576+ }
577+ if router == nil {
578+ log.Debug().Msg("router not available, skipping accent handler registration")
563579 return
564580 }
565581
566- if err := handlers.RegisterAccentHandlers(ctx, a.router, a.insertAccentUC); err != nil {
582+ if err := handlers.RegisterAccentHandlers(ctx, router, a.insertAccentUC); err != nil {
567583 log.Error().Err(err).Msg("failed to register accent handlers")
568584 } else {
569585 log.Info().Msg("registered accent key handlers")
@@ -1492,8 +1508,10 @@ func (a *App) onShutdown(ctx context.Context) {
14921508 for _, tabID := range tabIDs {
14931509 a.releaseFloatingSessionsForTab(ctx, tabID)
14941510 }
1495- if a.pool != nil {
1496- a.pool.Close(ctx)
1511+ if a.engine != nil {
1512+ if err := a.engine.Close(); err != nil {
1513+ log.Warn().Err(err).Msg("failed to close engine")
1514+ }
14971515 }
14981516 // Close idle inhibitor to release D-Bus connection
14991517 if a.deps.IdleInhibitor != nil {
@@ -1515,17 +1533,14 @@ func (a *App) initCoordinators(ctx context.Context) {
15151533 return a.activeWorkspace(), a.activeWorkspaceView()
15161534 }
15171535
1518- // Create FaviconAdapter with service and WebKit FaviconDatabase
1519- var faviconDB *webkit.FaviconDatabase
1520- if a.wkEngine != nil {
1521- faviconDB = a.wkEngine.InternalContext().FaviconDatabase()
1522- }
1523- a.faviconAdapter = adapter.NewFaviconAdapter(a.deps.FaviconService, faviconDB)
1536+ // Create FaviconAdapter with service and engine FaviconDatabase
1537+ a.faviconAdapter = adapter.NewFaviconAdapter(a.deps.FaviconService, a.engine.FaviconDatabase())
15241538
15251539 // 1. Content Coordinator (no dependencies on other coordinators)
15261540 a.contentCoord = content.NewCoordinator(
15271541 ctx,
15281542 a.engine.Pool(),
1543+ a.engine.ContentInjector(),
15291544 a.widgetFactory,
15301545 a.faviconAdapter,
15311546 getActiveWS,
@@ -1602,21 +1617,12 @@ func (a *App) initCoordinators(ctx context.Context) {
16021617 a.wsCoord.SetOnStateChanged(a.MarkDirty)
16031618
16041619 // Wire popup handling
1605- var wkCtx *webkit.WebKitContext
1606- if a.wkEngine != nil {
1607- wkCtx = a.wkEngine.InternalContext()
1608- }
1609- a.webViewFactory = webkit.NewWebViewFactory(
1610- wkCtx,
1611- a.settings,
1612- a.pool,
1613- a.injector,
1614- a.router,
1615- )
1616- // Set theme background color on factory to eliminate white flash
1617- if a.deps.Theme != nil {
1620+ // Set theme background color on the engine's popup factory to eliminate white flash.
1621+ if a.wkEngine != nil && a.deps.Theme != nil {
16181622 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()
1619- a.webViewFactory.SetBackgroundColor(r, g, b, alpha)
1623+ if f := a.wkEngine.InternalFactory(); f != nil {
1624+ f.SetBackgroundColor(r, g, b, alpha)
1625+ }
16201626 }
16211627 a.contentCoord.SetPopupConfig(
16221628 a.engine.Factory(),
@@ -3095,8 +3101,10 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
30953101 }
30963102
30973103 // Update WebKit settings manager
3098- if a.settings != nil {
3099- a.settings.UpdateFromConfig(ctx, cfg)
3104+ if a.wkEngine != nil {
3105+ if settings := a.wkEngine.InternalSettings(); settings != nil {
3106+ settings.UpdateFromConfig(ctx, cfg)
3107+ }
31003108 }
31013109
31023110 // Apply settings to existing webviews via coordinator
@@ -3112,22 +3120,28 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
31123120 }
31133121 a.deps.Theme.UpdateFromConfig(ctx, cfg, display)
31143122
3123+ // Resolve injector once for all webkit-specific theme updates below.
3124+ var injector *webkit.ContentInjector
3125+ if a.wkEngine != nil {
3126+ injector = a.wkEngine.InternalInjector()
3127+ }
3128+
31153129 // Update find highlight CSS for future navigations
3116- if a.injector != nil {
3130+ if injector != nil {
31173131 findCSS := theme.GenerateFindHighlightCSS(a.deps.Theme.GetCurrentPalette())
3118- if err := a.injector.InjectFindHighlightCSS(ctx, findCSS); err != nil {
3132+ if err := injector.InjectFindHighlightCSS(ctx, findCSS); err != nil {
31193133 log.Warn().Err(err).Msg("failed to update find highlight CSS")
31203134 }
31213135 }
31223136
3123- prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(a.injector)
3137+ prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
31243138 cssText := a.deps.Theme.GetWebUIThemeCSS()
31253139 if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: cssText}); err != nil {
31263140 log.Warn().Err(err).Msg("failed to update WebUI theme CSS")
31273141 }
31283142
31293143 // Refresh injected scripts so future navigations use the latest theme.
3130- if a.contentCoord != nil && a.injector != nil {
3144+ if a.contentCoord != nil && injector != nil {
31313145 a.contentCoord.RefreshInjectedScriptsToAll(ctx)
31323146 }
31333147
@@ -102,14 +102,9 @@ type App struct {
102 102
103 // Engine (port-level and concrete for webkit-specific operations)103 // Engine (port-level and concrete for webkit-specific operations)
104 engine port.Engine104 engine port.Engine
105- wkEngine *webkit.Engine105+ wkEngine *webkit.Engine // single webkit escape hatch; all webkit-specific ops go through this
106 106
107 // Web content (managed by content.Coordinator)107 // Web content (managed by content.Coordinator)
108- pool *webkit.WebViewPool
109- webViewFactory *webkit.WebViewFactory
110- injector *webkit.ContentInjector
111- router *webkit.MessageRouter
112- settings *webkit.SettingsManager
113 faviconAdapter *adapter.FaviconAdapter108 faviconAdapter *adapter.FaviconAdapter
114 109
115 // App-level toaster for system notifications (filter status, etc.)110 // App-level toaster for system notifications (filter status, etc.)
@@ -191,18 +186,20 @@ func New(deps *Dependencies) (*App, error) {
191 // Type-assert engine to concrete *webkit.Engine for webkit-specific operations.186 // Type-assert engine to concrete *webkit.Engine for webkit-specific operations.
192 if wke, ok := deps.Engine.(*webkit.Engine); ok {187 if wke, ok := deps.Engine.(*webkit.Engine); ok {
193 app.wkEngine = wke188 app.wkEngine = wke
194- app.pool = wke.InternalPool()
195- app.injector = wke.InternalInjector()
196- app.router = wke.InternalMessageRouter()
197- app.settings = wke.InternalSettings()
198 }189 }
199- if app.router == nil {190+
200- app.router = webkit.NewMessageRouter(ctx)191+ // Resolve the message router via wkEngine, with a standalone fallback.
192+ var router *webkit.MessageRouter
193+ if app.wkEngine != nil {
194+ router = app.wkEngine.InternalMessageRouter()
195+ }
196+ if router == nil {
197+ router = webkit.NewMessageRouter(ctx)
201 }198 }
202 199
203 // Register message handlers200 // Register message handlers
204- if app.router != nil {201+ if router != nil {
205- if err := handlers.RegisterAll(ctx, app.router, handlers.Config{202+ if err := handlers.RegisterAll(ctx, router, handlers.Config{
206 HistoryUC: deps.HistoryUC,203 HistoryUC: deps.HistoryUC,
207 FavoritesUC: deps.FavoritesUC,204 FavoritesUC: deps.FavoritesUC,
208 Clipboard: deps.Clipboard,205 Clipboard: deps.Clipboard,
@@ -249,8 +246,10 @@ func (a *App) Run(ctx context.Context, args []string) int {
249 246
250 // Refresh scripts in pooled WebViews that were prewarmed before adw.Init().247 // Refresh scripts in pooled WebViews that were prewarmed before adw.Init().
251 // They have the wrong dark mode preference injected; re-inject with correct value.248 // They have the wrong dark mode preference injected; re-inject with correct value.
252- if a.pool != nil {249+ if a.wkEngine != nil {
253- a.pool.RefreshScripts(ctx)250+ if pool := a.wkEngine.InternalPool(); pool != nil {
251+ pool.RefreshScripts(ctx)
252+ }
254 }253 }
255 }254 }
256 255
@@ -375,7 +374,12 @@ func (a *App) setupPoolBackgroundColor(ctx context.Context) {
375 374
376 // Set theme background color on pool to eliminate white flash.375 // Set theme background color on pool to eliminate white flash.
377 // Must be done before any WebView creation so WebViews get the correct color.376 // Must be done before any WebView creation so WebViews get the correct color.
378- if a.pool == nil {377+ if a.wkEngine == nil {
378+ log.Debug().Msg("webview pool not available; skipping background color setup")
379+ return
380+ }
381+ pool := a.wkEngine.InternalPool()
382+ if pool == nil {
379 log.Debug().Msg("webview pool not available; skipping background color setup")383 log.Debug().Msg("webview pool not available; skipping background color setup")
380 return384 return
381 }385 }
@@ -385,16 +389,20 @@ func (a *App) setupPoolBackgroundColor(ctx context.Context) {
385 }389 }
386 390
387 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()391 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()
388- a.pool.SetBackgroundColor(r, g, b, alpha)392+ pool.SetBackgroundColor(r, g, b, alpha)
389 log.Debug().Msg("configured webview pool background color")393 log.Debug().Msg("configured webview pool background color")
390 }394 }
391 395
392 func (a *App) prewarmWebViewPoolAsync(ctx context.Context) {396 func (a *App) prewarmWebViewPoolAsync(ctx context.Context) {
393 // Prewarm WebView pool after startup so cold-start navigation is not blocked.397 // Prewarm WebView pool after startup so cold-start navigation is not blocked.
394- if a.pool == nil {398+ if a.wkEngine == nil {
395 return399 return
396 }400 }
397- a.pool.PrewarmAsync(ctx, 0)401+ pool := a.wkEngine.InternalPool()
402+ if pool == nil {
403+ return
404+ }
405+ pool.PrewarmAsync(ctx, 0)
398 }406 }
399 407
400 func (a *App) createMainWindow(ctx context.Context) error {408 func (a *App) createMainWindow(ctx context.Context) error {
@@ -558,12 +566,20 @@ func (a *App) initAccentPicker(ctx context.Context) {
558 func (a *App) registerAccentHandlers(ctx context.Context) {566 func (a *App) registerAccentHandlers(ctx context.Context) {
559 log := logging.FromContext(ctx)567 log := logging.FromContext(ctx)
560 568
561- if a.router == nil || a.insertAccentUC == nil {569+ if a.insertAccentUC == nil {
562- log.Debug().Msg("router or insertAccentUC not available, skipping accent handler registration")570+ log.Debug().Msg("insertAccentUC not available, skipping accent handler registration")
571+ return
572+ }
573+ var router *webkit.MessageRouter
574+ if a.wkEngine != nil {
575+ router = a.wkEngine.InternalMessageRouter()
576+ }
577+ if router == nil {
578+ log.Debug().Msg("router not available, skipping accent handler registration")
563 return579 return
564 }580 }
565 581
566- if err := handlers.RegisterAccentHandlers(ctx, a.router, a.insertAccentUC); err != nil {582+ if err := handlers.RegisterAccentHandlers(ctx, router, a.insertAccentUC); err != nil {
567 log.Error().Err(err).Msg("failed to register accent handlers")583 log.Error().Err(err).Msg("failed to register accent handlers")
568 } else {584 } else {
569 log.Info().Msg("registered accent key handlers")585 log.Info().Msg("registered accent key handlers")
@@ -1492,8 +1508,10 @@ func (a *App) onShutdown(ctx context.Context) {
1492 for _, tabID := range tabIDs {1508 for _, tabID := range tabIDs {
1493 a.releaseFloatingSessionsForTab(ctx, tabID)1509 a.releaseFloatingSessionsForTab(ctx, tabID)
1494 }1510 }
1495- if a.pool != nil {1511+ if a.engine != nil {
1496- a.pool.Close(ctx)1512+ if err := a.engine.Close(); err != nil {
1513+ log.Warn().Err(err).Msg("failed to close engine")
1514+ }
1497 }1515 }
1498 // Close idle inhibitor to release D-Bus connection1516 // Close idle inhibitor to release D-Bus connection
1499 if a.deps.IdleInhibitor != nil {1517 if a.deps.IdleInhibitor != nil {
@@ -1515,17 +1533,14 @@ func (a *App) initCoordinators(ctx context.Context) {
1515 return a.activeWorkspace(), a.activeWorkspaceView()1533 return a.activeWorkspace(), a.activeWorkspaceView()
1516 }1534 }
1517 1535
1518- // Create FaviconAdapter with service and WebKit FaviconDatabase1536+ // Create FaviconAdapter with service and engine FaviconDatabase
1519- var faviconDB *webkit.FaviconDatabase1537+ a.faviconAdapter = adapter.NewFaviconAdapter(a.deps.FaviconService, a.engine.FaviconDatabase())
1520- if a.wkEngine != nil {
1521- faviconDB = a.wkEngine.InternalContext().FaviconDatabase()
1522- }
1523- a.faviconAdapter = adapter.NewFaviconAdapter(a.deps.FaviconService, faviconDB)
1524 1538
1525 // 1. Content Coordinator (no dependencies on other coordinators)1539 // 1. Content Coordinator (no dependencies on other coordinators)
1526 a.contentCoord = content.NewCoordinator(1540 a.contentCoord = content.NewCoordinator(
1527 ctx,1541 ctx,
1528 a.engine.Pool(),1542 a.engine.Pool(),
1543+ a.engine.ContentInjector(),
1529 a.widgetFactory,1544 a.widgetFactory,
1530 a.faviconAdapter,1545 a.faviconAdapter,
1531 getActiveWS,1546 getActiveWS,
@@ -1602,21 +1617,12 @@ func (a *App) initCoordinators(ctx context.Context) {
1602 a.wsCoord.SetOnStateChanged(a.MarkDirty)1617 a.wsCoord.SetOnStateChanged(a.MarkDirty)
1603 1618
1604 // Wire popup handling1619 // Wire popup handling
1605- var wkCtx *webkit.WebKitContext1620+ // Set theme background color on the engine's popup factory to eliminate white flash.
1606- if a.wkEngine != nil {1621+ if a.wkEngine != nil && a.deps.Theme != nil {
1607- wkCtx = a.wkEngine.InternalContext()
1608- }
1609- a.webViewFactory = webkit.NewWebViewFactory(
1610- wkCtx,
1611- a.settings,
1612- a.pool,
1613- a.injector,
1614- a.router,
1615- )
1616- // Set theme background color on factory to eliminate white flash
1617- if a.deps.Theme != nil {
1618 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()1622 r, g, b, alpha := a.deps.Theme.GetBackgroundRGBA()
1619- a.webViewFactory.SetBackgroundColor(r, g, b, alpha)1623+ if f := a.wkEngine.InternalFactory(); f != nil {
1624+ f.SetBackgroundColor(r, g, b, alpha)
1625+ }
1620 }1626 }
1621 a.contentCoord.SetPopupConfig(1627 a.contentCoord.SetPopupConfig(
1622 a.engine.Factory(),1628 a.engine.Factory(),
@@ -3095,8 +3101,10 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
3095 }3101 }
3096 3102
3097 // Update WebKit settings manager3103 // Update WebKit settings manager
3098- if a.settings != nil {3104+ if a.wkEngine != nil {
3099- a.settings.UpdateFromConfig(ctx, cfg)3105+ if settings := a.wkEngine.InternalSettings(); settings != nil {
3106+ settings.UpdateFromConfig(ctx, cfg)
3107+ }
3100 }3108 }
3101 3109
3102 // Apply settings to existing webviews via coordinator3110 // Apply settings to existing webviews via coordinator
@@ -3112,22 +3120,28 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
3112 }3120 }
3113 a.deps.Theme.UpdateFromConfig(ctx, cfg, display)3121 a.deps.Theme.UpdateFromConfig(ctx, cfg, display)
3114 3122
3123+ // Resolve injector once for all webkit-specific theme updates below.
3124+ var injector *webkit.ContentInjector
3125+ if a.wkEngine != nil {
3126+ injector = a.wkEngine.InternalInjector()
3127+ }
3128+
3115 // Update find highlight CSS for future navigations3129 // Update find highlight CSS for future navigations
3116- if a.injector != nil {3130+ if injector != nil {
3117 findCSS := theme.GenerateFindHighlightCSS(a.deps.Theme.GetCurrentPalette())3131 findCSS := theme.GenerateFindHighlightCSS(a.deps.Theme.GetCurrentPalette())
3118- if err := a.injector.InjectFindHighlightCSS(ctx, findCSS); err != nil {3132+ if err := injector.InjectFindHighlightCSS(ctx, findCSS); err != nil {
3119 log.Warn().Err(err).Msg("failed to update find highlight CSS")3133 log.Warn().Err(err).Msg("failed to update find highlight CSS")
3120 }3134 }
3121 }3135 }
3122 3136
3123- prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(a.injector)3137+ prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
3124 cssText := a.deps.Theme.GetWebUIThemeCSS()3138 cssText := a.deps.Theme.GetWebUIThemeCSS()
3125 if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: cssText}); err != nil {3139 if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: cssText}); err != nil {
3126 log.Warn().Err(err).Msg("failed to update WebUI theme CSS")3140 log.Warn().Err(err).Msg("failed to update WebUI theme CSS")
3127 }3141 }
3128 3142
3129 // Refresh injected scripts so future navigations use the latest theme.3143 // Refresh injected scripts so future navigations use the latest theme.
3130- if a.contentCoord != nil && a.injector != nil {3144+ if a.contentCoord != nil && injector != nil {
3131 a.contentCoord.RefreshInjectedScriptsToAll(ctx)3145 a.contentCoord.RefreshInjectedScriptsToAll(ctx)
3132 }3146 }
3133 3147
M internal/ui/coordinator/content/appearance.go +12 -22
@@ -9,7 +9,6 @@ import (
99
1010 "github.com/bnema/dumber/internal/application/port"
1111 "github.com/bnema/dumber/internal/domain/entity"
12- "github.com/bnema/dumber/internal/infrastructure/webkit"
1312 "github.com/bnema/dumber/internal/logging"
1413 )
1514
@@ -42,7 +41,15 @@ func (c *Coordinator) ApplyWebUIThemeToAll(ctx context.Context, prefersDark bool
4241 return
4342 }
4443
45- for paneID, wv := range c.webViews {
44+ // Snapshot webViews under lock to avoid data race with concurrent popup create/close.
45+ c.webViewsMu.RLock()
46+ snapshot := make(map[entity.PaneID]port.WebView, len(c.webViews))
47+ for k, v := range c.webViews {
48+ snapshot[k] = v
49+ }
50+ c.webViewsMu.RUnlock()
51+
52+ for paneID, wv := range snapshot {
4653 if wv == nil || wv.IsDestroyed() {
4754 continue
4855 }
@@ -126,10 +133,7 @@ func (c *Coordinator) applyWebUITheme(
126133 if !strings.HasPrefix(uri, "dumb://") {
127134 return
128135 }
129- // Type-assert to access webkit-specific RunJavaScript
130- if wkWV, ok := wv.(*webkit.WebView); ok {
131- wkWV.RunJavaScript(ctx, script, "")
132- }
136+ wv.RunJavaScript(ctx, script)
133137 logging.FromContext(ctx).
134138 Debug().
135139 Str("pane_id", string(paneID)).
@@ -269,24 +273,10 @@ func (c *Coordinator) refreshInjectedScripts(
269273 if injector == nil || wv == nil || wv.IsDestroyed() {
270274 return
271275 }
272- // Type-assert to access webkit-specific UserContentManager and InjectScripts.
273- // refreshInjectedScripts is a webkit-specific operation; other engines will
274- // need their own implementation when wired through a port.ScriptRefresher.
275- wkInjector, ok := injector.(*webkit.ContentInjector)
276- if !ok {
277- return
278- }
279- wkWV, ok := wv.(*webkit.WebView)
280- if !ok {
281- return
282- }
283- ucm := wkWV.UserContentManager()
284- if ucm == nil {
276+ if err := injector.RefreshScripts(ctx, wv); err != nil {
277+ logging.FromContext(ctx).Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to refresh injected scripts")
285278 return
286279 }
287- ucm.RemoveAllScripts()
288- ucm.RemoveAllStyleSheets()
289- wkInjector.InjectScripts(ctx, ucm, wv.ID())
290280 logging.FromContext(ctx).Debug().Str("pane_id", string(paneID)).Msg("refreshed injected scripts for webview")
291281 }
292282
@@ -9,7 +9,6 @@ import (
9 9
10 "github.com/bnema/dumber/internal/application/port"10 "github.com/bnema/dumber/internal/application/port"
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12- "github.com/bnema/dumber/internal/infrastructure/webkit"
13 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
14 )13 )
15 14
@@ -42,7 +41,15 @@ func (c *Coordinator) ApplyWebUIThemeToAll(ctx context.Context, prefersDark bool
42 return41 return
43 }42 }
44 43
45- for paneID, wv := range c.webViews {44+ // Snapshot webViews under lock to avoid data race with concurrent popup create/close.
45+ c.webViewsMu.RLock()
46+ snapshot := make(map[entity.PaneID]port.WebView, len(c.webViews))
47+ for k, v := range c.webViews {
48+ snapshot[k] = v
49+ }
50+ c.webViewsMu.RUnlock()
51+
52+ for paneID, wv := range snapshot {
46 if wv == nil || wv.IsDestroyed() {53 if wv == nil || wv.IsDestroyed() {
47 continue54 continue
48 }55 }
@@ -126,10 +133,7 @@ func (c *Coordinator) applyWebUITheme(
126 if !strings.HasPrefix(uri, "dumb://") {133 if !strings.HasPrefix(uri, "dumb://") {
127 return134 return
128 }135 }
129- // Type-assert to access webkit-specific RunJavaScript136+ wv.RunJavaScript(ctx, script)
130- if wkWV, ok := wv.(*webkit.WebView); ok {
131- wkWV.RunJavaScript(ctx, script, "")
132- }
133 logging.FromContext(ctx).137 logging.FromContext(ctx).
134 Debug().138 Debug().
135 Str("pane_id", string(paneID)).139 Str("pane_id", string(paneID)).
@@ -269,24 +273,10 @@ func (c *Coordinator) refreshInjectedScripts(
269 if injector == nil || wv == nil || wv.IsDestroyed() {273 if injector == nil || wv == nil || wv.IsDestroyed() {
270 return274 return
271 }275 }
272- // Type-assert to access webkit-specific UserContentManager and InjectScripts.276+ if err := injector.RefreshScripts(ctx, wv); err != nil {
273- // refreshInjectedScripts is a webkit-specific operation; other engines will277+ logging.FromContext(ctx).Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to refresh injected scripts")
274- // need their own implementation when wired through a port.ScriptRefresher.
275- wkInjector, ok := injector.(*webkit.ContentInjector)
276- if !ok {
277- return
278- }
279- wkWV, ok := wv.(*webkit.WebView)
280- if !ok {
281- return
282- }
283- ucm := wkWV.UserContentManager()
284- if ucm == nil {
285 return278 return
286 }279 }
287- ucm.RemoveAllScripts()
288- ucm.RemoveAllStyleSheets()
289- wkInjector.InjectScripts(ctx, ucm, wv.ID())
290 logging.FromContext(ctx).Debug().Str("pane_id", string(paneID)).Msg("refreshed injected scripts for webview")280 logging.FromContext(ctx).Debug().Str("pane_id", string(paneID)).Msg("refreshed injected scripts for webview")
291 }281 }
292 282
M internal/ui/coordinator/content/bulk.go +11 -1
@@ -3,6 +3,8 @@ package content
33 import (
44 "context"
55
6+ "github.com/bnema/dumber/internal/application/port"
7+ "github.com/bnema/dumber/internal/domain/entity"
68 "github.com/bnema/dumber/internal/logging"
79 )
810
@@ -35,7 +37,15 @@ func (c *Coordinator) RefreshInjectedScriptsToAll(ctx context.Context) {
3537 return
3638 }
3739
38- for paneID, wv := range c.webViews {
40+ // Snapshot webViews under lock to avoid data race with concurrent popup create/close.
41+ c.webViewsMu.RLock()
42+ snapshot := make(map[entity.PaneID]port.WebView, len(c.webViews))
43+ for k, v := range c.webViews {
44+ snapshot[k] = v
45+ }
46+ c.webViewsMu.RUnlock()
47+
48+ for paneID, wv := range snapshot {
3949 if wv == nil || wv.IsDestroyed() {
4050 continue
4151 }
@@ -3,6 +3,8 @@ package content
3 import (3 import (
4 "context"4 "context"
5 5
6+ "github.com/bnema/dumber/internal/application/port"
7+ "github.com/bnema/dumber/internal/domain/entity"
6 "github.com/bnema/dumber/internal/logging"8 "github.com/bnema/dumber/internal/logging"
7 )9 )
8 10
@@ -35,7 +37,15 @@ func (c *Coordinator) RefreshInjectedScriptsToAll(ctx context.Context) {
35 return37 return
36 }38 }
37 39
38- for paneID, wv := range c.webViews {40+ // Snapshot webViews under lock to avoid data race with concurrent popup create/close.
41+ c.webViewsMu.RLock()
42+ snapshot := make(map[entity.PaneID]port.WebView, len(c.webViews))
43+ for k, v := range c.webViews {
44+ snapshot[k] = v
45+ }
46+ c.webViewsMu.RUnlock()
47+
48+ for paneID, wv := range snapshot {
39 if wv == nil || wv.IsDestroyed() {49 if wv == nil || wv.IsDestroyed() {
40 continue50 continue
41 }51 }
M internal/ui/coordinator/content/callbacks.go +52 -79
@@ -8,7 +8,6 @@ import (
88 "github.com/bnema/dumber/internal/application/port"
99 "github.com/bnema/dumber/internal/application/usecase"
1010 "github.com/bnema/dumber/internal/domain/entity"
11- "github.com/bnema/dumber/internal/infrastructure/webkit"
1211 "github.com/bnema/dumber/internal/logging"
1312 "github.com/jwijenbergh/puregotk/v4/gdk"
1413 )
@@ -123,15 +122,59 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
123122 },
124123 }
125124
126- // Favicon changes - need webkit-specific Generation() to detect stale callbacks
127- if wkWV, ok := wv.(*webkit.WebView); ok {
128- faviconGen := wkWV.Generation()
129- callbacks.OnFaviconChanged = func(favicon port.Texture) {
130- if wkWV.Generation() != faviconGen {
131- return // WebView was reused, ignore stale signal
125+ // Favicon changes - use Generation() from port.WebView to detect stale callbacks
126+ faviconGen := wv.Generation()
127+ callbacks.OnFaviconChanged = func(favicon port.Texture) {
128+ if wv.Generation() != faviconGen {
129+ return // WebView was reused, ignore stale signal
130+ }
131+ if gdkTexture, ok := favicon.(*gdk.Texture); ok {
132+ c.onFaviconChanged(ctx, paneID, wv, gdkTexture)
133+ }
134+ }
135+
136+ // Middle-click link handler
137+ callbacks.OnLinkMiddleClick = func(uri string) bool {
138+ return c.handleLinkMiddleClick(ctx, paneID, uri)
139+ }
140+
141+ // Fullscreen handlers for idle inhibition
142+ callbacks.OnEnterFullscreen = func() bool {
143+ if c.idleInhibitor != nil {
144+ if err := c.idleInhibitor.Inhibit(ctx, "Fullscreen video playback"); err != nil {
145+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle")
146+ }
147+ }
148+ if c.onFullscreenChanged != nil {
149+ c.onFullscreenChanged(true)
150+ }
151+ return false // Allow fullscreen
152+ }
153+
154+ callbacks.OnLeaveFullscreen = func() bool {
155+ if c.idleInhibitor != nil {
156+ if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
157+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle")
158+ }
159+ }
160+ if c.onFullscreenChanged != nil {
161+ c.onFullscreenChanged(false)
162+ }
163+ return false // Allow leaving fullscreen
164+ }
165+
166+ // Audio playback handling
167+ callbacks.OnAudioStateChanged = func(playing bool) {
168+ if c.idleInhibitor == nil {
169+ return
170+ }
171+ if playing {
172+ if err := c.idleInhibitor.Inhibit(ctx, "Media playback"); err != nil {
173+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle for audio")
132174 }
133- if gdkTexture, ok := favicon.(*gdk.Texture); ok {
134- c.onFaviconChanged(ctx, paneID, wkWV, gdkTexture)
175+ } else {
176+ if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
177+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle for audio")
135178 }
136179 }
137180 }
@@ -140,16 +183,6 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
140183 callbacks.OnCreate = c.buildPopupCreateHandler(ctx, paneID, wv)
141184
142185 wv.SetCallbacks(callbacks)
143-
144- // Webkit-specific callbacks not in port.WebViewCallbacks (middle-click, fullscreen, audio)
145- if wkWV, ok := wv.(*webkit.WebView); ok {
146- wkWV.OnLinkMiddleClick = func(uri string) bool {
147- return c.handleLinkMiddleClick(ctx, paneID, uri)
148- }
149- }
150-
151- // Fullscreen handlers for idle inhibition
152- c.setupIdleInhibitionHandlers(ctx, paneID, wv)
153186 }
154187
155188 // handlePermissionRequest processes media permission requests from WebKit.
@@ -243,63 +276,3 @@ func filterWebRTCPermissionTypes(types []entity.PermissionType) []entity.Permiss
243276 }
244277 return filtered
245278 }
246-
247-// setupIdleInhibitionHandlers configures fullscreen and audio callbacks for idle inhibition.
248-// Idle is inhibited when:
249-// - The webview enters fullscreen mode (e.g., fullscreen video)
250-// - The webview is playing audio (e.g., video/music playback)
251-// The inhibitor uses refcounting, so both can be active simultaneously.
252-func (c *Coordinator) setupIdleInhibitionHandlers(ctx context.Context, paneID entity.PaneID, wv port.WebView) {
253- log := logging.FromContext(ctx)
254-
255- if wv == nil {
256- return
257- }
258-
259- // Type-assert to access webkit-specific fullscreen/audio callbacks
260- wkWV, ok := wv.(*webkit.WebView)
261- if !ok {
262- return
263- }
264-
265- // Fullscreen handling
266- wkWV.OnEnterFullscreen = func() bool {
267- if c.idleInhibitor != nil {
268- if err := c.idleInhibitor.Inhibit(ctx, "Fullscreen video playback"); err != nil {
269- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle")
270- }
271- }
272- if c.onFullscreenChanged != nil {
273- c.onFullscreenChanged(true)
274- }
275- return false // Allow fullscreen
276- }
277-
278- wkWV.OnLeaveFullscreen = func() bool {
279- if c.idleInhibitor != nil {
280- if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
281- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle")
282- }
283- }
284- if c.onFullscreenChanged != nil {
285- c.onFullscreenChanged(false)
286- }
287- return false // Allow leaving fullscreen
288- }
289-
290- // Audio playback handling
291- wkWV.OnAudioStateChanged = func(playing bool) {
292- if c.idleInhibitor == nil {
293- return
294- }
295- if playing {
296- if err := c.idleInhibitor.Inhibit(ctx, "Media playback"); err != nil {
297- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle for audio")
298- }
299- } else {
300- if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
301- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle for audio")
302- }
303- }
304- }
305-}
@@ -8,7 +8,6 @@ import (
8 "github.com/bnema/dumber/internal/application/port"8 "github.com/bnema/dumber/internal/application/port"
9 "github.com/bnema/dumber/internal/application/usecase"9 "github.com/bnema/dumber/internal/application/usecase"
10 "github.com/bnema/dumber/internal/domain/entity"10 "github.com/bnema/dumber/internal/domain/entity"
11- "github.com/bnema/dumber/internal/infrastructure/webkit"
12 "github.com/bnema/dumber/internal/logging"11 "github.com/bnema/dumber/internal/logging"
13 "github.com/jwijenbergh/puregotk/v4/gdk"12 "github.com/jwijenbergh/puregotk/v4/gdk"
14 )13 )
@@ -123,15 +122,59 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
123 },122 },
124 }123 }
125 124
126- // Favicon changes - need webkit-specific Generation() to detect stale callbacks125+ // Favicon changes - use Generation() from port.WebView to detect stale callbacks
127- if wkWV, ok := wv.(*webkit.WebView); ok {126+ faviconGen := wv.Generation()
128- faviconGen := wkWV.Generation()127+ callbacks.OnFaviconChanged = func(favicon port.Texture) {
129- callbacks.OnFaviconChanged = func(favicon port.Texture) {128+ if wv.Generation() != faviconGen {
130- if wkWV.Generation() != faviconGen {129+ return // WebView was reused, ignore stale signal
131- return // WebView was reused, ignore stale signal130+ }
131+ if gdkTexture, ok := favicon.(*gdk.Texture); ok {
132+ c.onFaviconChanged(ctx, paneID, wv, gdkTexture)
133+ }
134+ }
135+
136+ // Middle-click link handler
137+ callbacks.OnLinkMiddleClick = func(uri string) bool {
138+ return c.handleLinkMiddleClick(ctx, paneID, uri)
139+ }
140+
141+ // Fullscreen handlers for idle inhibition
142+ callbacks.OnEnterFullscreen = func() bool {
143+ if c.idleInhibitor != nil {
144+ if err := c.idleInhibitor.Inhibit(ctx, "Fullscreen video playback"); err != nil {
145+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle")
146+ }
147+ }
148+ if c.onFullscreenChanged != nil {
149+ c.onFullscreenChanged(true)
150+ }
151+ return false // Allow fullscreen
152+ }
153+
154+ callbacks.OnLeaveFullscreen = func() bool {
155+ if c.idleInhibitor != nil {
156+ if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
157+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle")
158+ }
159+ }
160+ if c.onFullscreenChanged != nil {
161+ c.onFullscreenChanged(false)
162+ }
163+ return false // Allow leaving fullscreen
164+ }
165+
166+ // Audio playback handling
167+ callbacks.OnAudioStateChanged = func(playing bool) {
168+ if c.idleInhibitor == nil {
169+ return
170+ }
171+ if playing {
172+ if err := c.idleInhibitor.Inhibit(ctx, "Media playback"); err != nil {
173+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle for audio")
132 }174 }
133- if gdkTexture, ok := favicon.(*gdk.Texture); ok {175+ } else {
134- c.onFaviconChanged(ctx, paneID, wkWV, gdkTexture)176+ if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
177+ log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle for audio")
135 }178 }
136 }179 }
137 }180 }
@@ -140,16 +183,6 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
140 callbacks.OnCreate = c.buildPopupCreateHandler(ctx, paneID, wv)183 callbacks.OnCreate = c.buildPopupCreateHandler(ctx, paneID, wv)
141 184
142 wv.SetCallbacks(callbacks)185 wv.SetCallbacks(callbacks)
143-
144- // Webkit-specific callbacks not in port.WebViewCallbacks (middle-click, fullscreen, audio)
145- if wkWV, ok := wv.(*webkit.WebView); ok {
146- wkWV.OnLinkMiddleClick = func(uri string) bool {
147- return c.handleLinkMiddleClick(ctx, paneID, uri)
148- }
149- }
150-
151- // Fullscreen handlers for idle inhibition
152- c.setupIdleInhibitionHandlers(ctx, paneID, wv)
153 }186 }
154 187
155 // handlePermissionRequest processes media permission requests from WebKit.188 // handlePermissionRequest processes media permission requests from WebKit.
@@ -243,63 +276,3 @@ func filterWebRTCPermissionTypes(types []entity.PermissionType) []entity.Permiss
243 }276 }
244 return filtered277 return filtered
245 }278 }
246-
247-// setupIdleInhibitionHandlers configures fullscreen and audio callbacks for idle inhibition.
248-// Idle is inhibited when:
249-// - The webview enters fullscreen mode (e.g., fullscreen video)
250-// - The webview is playing audio (e.g., video/music playback)
251-// The inhibitor uses refcounting, so both can be active simultaneously.
252-func (c *Coordinator) setupIdleInhibitionHandlers(ctx context.Context, paneID entity.PaneID, wv port.WebView) {
253- log := logging.FromContext(ctx)
254-
255- if wv == nil {
256- return
257- }
258-
259- // Type-assert to access webkit-specific fullscreen/audio callbacks
260- wkWV, ok := wv.(*webkit.WebView)
261- if !ok {
262- return
263- }
264-
265- // Fullscreen handling
266- wkWV.OnEnterFullscreen = func() bool {
267- if c.idleInhibitor != nil {
268- if err := c.idleInhibitor.Inhibit(ctx, "Fullscreen video playback"); err != nil {
269- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle")
270- }
271- }
272- if c.onFullscreenChanged != nil {
273- c.onFullscreenChanged(true)
274- }
275- return false // Allow fullscreen
276- }
277-
278- wkWV.OnLeaveFullscreen = func() bool {
279- if c.idleInhibitor != nil {
280- if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
281- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle")
282- }
283- }
284- if c.onFullscreenChanged != nil {
285- c.onFullscreenChanged(false)
286- }
287- return false // Allow leaving fullscreen
288- }
289-
290- // Audio playback handling
291- wkWV.OnAudioStateChanged = func(playing bool) {
292- if c.idleInhibitor == nil {
293- return
294- }
295- if playing {
296- if err := c.idleInhibitor.Inhibit(ctx, "Media playback"); err != nil {
297- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to inhibit idle for audio")
298- }
299- } else {
300- if err := c.idleInhibitor.Uninhibit(ctx); err != nil {
301- log.Warn().Err(err).Str("pane_id", string(paneID)).Msg("failed to uninhibit idle for audio")
302- }
303- }
304- }
305-}
M internal/ui/coordinator/content/coordinator.go +2 -0
@@ -134,6 +134,7 @@ const (
134134 func NewCoordinator(
135135 ctx context.Context,
136136 pool port.WebViewPool,
137+ injector port.ContentInjector,
137138 widgetFactory layout.WidgetFactory,
138139 faviconAdapter *adapter.FaviconAdapter,
139140 getActiveWS func() (*entity.Workspace, *component.WorkspaceView),
@@ -145,6 +146,7 @@ func NewCoordinator(
145146
146147 return &Coordinator{
147148 pool: pool,
149+ injector: injector,
148150 widgetFactory: widgetFactory,
149151 faviconAdapter: faviconAdapter,
150152 zoomUC: zoomUC,
@@ -134,6 +134,7 @@ const (
134 func NewCoordinator(134 func NewCoordinator(
135 ctx context.Context,135 ctx context.Context,
136 pool port.WebViewPool,136 pool port.WebViewPool,
137+ injector port.ContentInjector,
137 widgetFactory layout.WidgetFactory,138 widgetFactory layout.WidgetFactory,
138 faviconAdapter *adapter.FaviconAdapter,139 faviconAdapter *adapter.FaviconAdapter,
139 getActiveWS func() (*entity.Workspace, *component.WorkspaceView),140 getActiveWS func() (*entity.Workspace, *component.WorkspaceView),
@@ -145,6 +146,7 @@ func NewCoordinator(
145 146
146 return &Coordinator{147 return &Coordinator{
147 pool: pool,148 pool: pool,
149+ injector: injector,
148 widgetFactory: widgetFactory,150 widgetFactory: widgetFactory,
149 faviconAdapter: faviconAdapter,151 faviconAdapter: faviconAdapter,
150 zoomUC: zoomUC,152 zoomUC: zoomUC,
M internal/ui/coordinator/content/lifecycle.go +8 -6
@@ -6,11 +6,11 @@ import (
66
77 "github.com/bnema/dumber/internal/application/port"
88 "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/infrastructure/webkit"
109 "github.com/bnema/dumber/internal/logging"
1110 "github.com/bnema/dumber/internal/ui/component"
1211 "github.com/bnema/dumber/internal/ui/input"
1312 "github.com/bnema/dumber/internal/ui/layout"
13+ "github.com/jwijenbergh/puregotk/v4/gtk"
1414 )
1515
1616 // EnsureWebView acquires or reuses a WebView for the given pane.
@@ -138,19 +138,21 @@ func (c *Coordinator) WrapWidget(ctx context.Context, wv port.WebView) layout.Wi
138138 return nil
139139 }
140140
141- // Type-assert to access webkit-specific Widget() for GTK embedding
142- wkWV, ok := wv.(*webkit.WebView)
141+ // Use NativeWidgetProvider interface for GTK embedding (engine-agnostic)
142+ nwp, ok := wv.(port.NativeWidgetProvider)
143143 if !ok {
144144 log.Debug().Msg("webview does not support widget embedding")
145145 return nil
146146 }
147147
148- gtkView := wkWV.Widget()
149- if gtkView == nil {
148+ ptr := nwp.NativeWidget()
149+ if ptr == 0 {
150150 return nil
151151 }
152152
153- widget := c.widgetFactory.WrapWidget(&gtkView.Widget)
153+ gtkWidget := &gtk.Widget{}
154+ gtkWidget.Ptr = ptr
155+ widget := c.widgetFactory.WrapWidget(gtkWidget)
154156
155157 // Attach gesture handler for mouse button 8/9 navigation
156158 if widget != nil {
@@ -6,11 +6,11 @@ import (
6 6
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/domain/entity"8 "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/infrastructure/webkit"
10 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
11 "github.com/bnema/dumber/internal/ui/component"10 "github.com/bnema/dumber/internal/ui/component"
12 "github.com/bnema/dumber/internal/ui/input"11 "github.com/bnema/dumber/internal/ui/input"
13 "github.com/bnema/dumber/internal/ui/layout"12 "github.com/bnema/dumber/internal/ui/layout"
13+ "github.com/jwijenbergh/puregotk/v4/gtk"
14 )14 )
15 15
16 // EnsureWebView acquires or reuses a WebView for the given pane.16 // EnsureWebView acquires or reuses a WebView for the given pane.
@@ -138,19 +138,21 @@ func (c *Coordinator) WrapWidget(ctx context.Context, wv port.WebView) layout.Wi
138 return nil138 return nil
139 }139 }
140 140
141- // Type-assert to access webkit-specific Widget() for GTK embedding141+ // Use NativeWidgetProvider interface for GTK embedding (engine-agnostic)
142- wkWV, ok := wv.(*webkit.WebView)142+ nwp, ok := wv.(port.NativeWidgetProvider)
143 if !ok {143 if !ok {
144 log.Debug().Msg("webview does not support widget embedding")144 log.Debug().Msg("webview does not support widget embedding")
145 return nil145 return nil
146 }146 }
147 147
148- gtkView := wkWV.Widget()148+ ptr := nwp.NativeWidget()
149- if gtkView == nil {149+ if ptr == 0 {
150 return nil150 return nil
151 }151 }
152 152
153- widget := c.widgetFactory.WrapWidget(&gtkView.Widget)153+ gtkWidget := &gtk.Widget{}
154+ gtkWidget.Ptr = ptr
155+ widget := c.widgetFactory.WrapWidget(gtkWidget)
154 156
155 // Attach gesture handler for mouse button 8/9 navigation157 // Attach gesture handler for mouse button 8/9 navigation
156 if widget != nil {158 if widget != nil {
M internal/ui/coordinator/content/lifecycle_test.go +2 -0
@@ -53,6 +53,7 @@ func TestLifecycle_EnsureWebView_AcquiresFromPoolWhenNoneExists(t *testing.T) {
5353
5454 newWV := mocks.NewMockWebView(t)
5555 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()
56+ newWV.EXPECT().Generation().Return(uint64(0)).Maybe()
5657
5758 pool := mocks.NewMockWebViewPool(t)
5859 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)
@@ -75,6 +76,7 @@ func TestLifecycle_EnsureWebView_AcquiresFromPoolWhenExistingIsDestroyed(t *test
7576
7677 newWV := mocks.NewMockWebView(t)
7778 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()
79+ newWV.EXPECT().Generation().Return(uint64(0)).Maybe()
7880
7981 pool := mocks.NewMockWebViewPool(t)
8082 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)
@@ -53,6 +53,7 @@ func TestLifecycle_EnsureWebView_AcquiresFromPoolWhenNoneExists(t *testing.T) {
53 53
54 newWV := mocks.NewMockWebView(t)54 newWV := mocks.NewMockWebView(t)
55 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()55 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()
56+ newWV.EXPECT().Generation().Return(uint64(0)).Maybe()
56 57
57 pool := mocks.NewMockWebViewPool(t)58 pool := mocks.NewMockWebViewPool(t)
58 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)59 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)
@@ -75,6 +76,7 @@ func TestLifecycle_EnsureWebView_AcquiresFromPoolWhenExistingIsDestroyed(t *test
75 76
76 newWV := mocks.NewMockWebView(t)77 newWV := mocks.NewMockWebView(t)
77 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()78 newWV.EXPECT().SetCallbacks(mock.Anything).Maybe()
79+ newWV.EXPECT().Generation().Return(uint64(0)).Maybe()
78 80
79 pool := mocks.NewMockWebViewPool(t)81 pool := mocks.NewMockWebViewPool(t)
80 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)82 pool.EXPECT().Acquire(mock.Anything).Return(newWV, nil)
M internal/ui/coordinator/content/navigation.go +20 -21
@@ -9,8 +9,8 @@ import (
99 "github.com/bnema/dumber/internal/domain/entity"
1010 urlutil "github.com/bnema/dumber/internal/domain/url"
1111 "github.com/bnema/dumber/internal/infrastructure/desktop"
12- "github.com/bnema/dumber/internal/infrastructure/webkit"
1312 "github.com/bnema/dumber/internal/logging"
13+ "github.com/jwijenbergh/puregotk/v4/gtk"
1414 )
1515
1616 // onLoadCommitted re-applies zoom when page content starts loading and records history.
@@ -27,23 +27,20 @@ func (c *Coordinator) onLoadCommitted(ctx context.Context, paneID entity.PaneID,
2727 }
2828
2929 // Set appropriate background color based on page type to prevent dark background bleeding.
30- // Type-assert to access webkit-specific background color methods.
31- if wkWV, ok := wv.(*webkit.WebView); ok {
32- switch {
33- case strings.HasPrefix(uri, "dumb://"):
34- // Internal pages: apply themed background
35- theme, ok := c.getCurrentTheme()
36- if ok && theme.prefersDark {
37- wkWV.SetBackgroundColor(darkBgR, darkBgG, darkBgB, darkBgA)
38- } else {
39- wkWV.ResetBackgroundToDefault()
40- }
41- case strings.HasPrefix(uri, "about:"):
42- // Keep pool background (no action)
43- default:
44- // External pages: white background
45- wkWV.ResetBackgroundToDefault()
30+ switch {
31+ case strings.HasPrefix(uri, "dumb://"):
32+ // Internal pages: apply themed background
33+ theme, ok := c.getCurrentTheme()
34+ if ok && theme.prefersDark {
35+ wv.SetBackgroundColor(darkBgR, darkBgG, darkBgB, darkBgA)
36+ } else {
37+ wv.ResetBackgroundToDefault()
4638 }
39+ case strings.HasPrefix(uri, "about:"):
40+ // Keep pool background (no action)
41+ default:
42+ // External pages: white background
43+ wv.ResetBackgroundToDefault()
4744 }
4845
4946 // Show the WebView now that content is being painted
@@ -259,10 +256,12 @@ func (c *Coordinator) revealIfPending(ctx context.Context, paneID entity.PaneID,
259256 return
260257 }
261258
262- // Type-assert to access webkit-specific Widget() for visibility control
263- if wkWV, ok := wv.(*webkit.WebView); ok {
264- if inner := wkWV.Widget(); inner != nil {
265- inner.SetVisible(true)
259+ // Use NativeWidgetProvider to make the widget visible (engine-agnostic)
260+ if nwp, ok := wv.(port.NativeWidgetProvider); ok {
261+ if ptr := nwp.NativeWidget(); ptr != 0 {
262+ gtkWidget := &gtk.Widget{}
263+ gtkWidget.Ptr = ptr
264+ gtkWidget.SetVisible(true)
266265 logging.FromContext(ctx).
267266 Debug().
268267 Str("pane_id", string(paneID)).
@@ -9,8 +9,8 @@ import (
9 "github.com/bnema/dumber/internal/domain/entity"9 "github.com/bnema/dumber/internal/domain/entity"
10 urlutil "github.com/bnema/dumber/internal/domain/url"10 urlutil "github.com/bnema/dumber/internal/domain/url"
11 "github.com/bnema/dumber/internal/infrastructure/desktop"11 "github.com/bnema/dumber/internal/infrastructure/desktop"
12- "github.com/bnema/dumber/internal/infrastructure/webkit"
13 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13+ "github.com/jwijenbergh/puregotk/v4/gtk"
14 )14 )
15 15
16 // onLoadCommitted re-applies zoom when page content starts loading and records history.16 // onLoadCommitted re-applies zoom when page content starts loading and records history.
@@ -27,23 +27,20 @@ func (c *Coordinator) onLoadCommitted(ctx context.Context, paneID entity.PaneID,
27 }27 }
28 28
29 // Set appropriate background color based on page type to prevent dark background bleeding.29 // Set appropriate background color based on page type to prevent dark background bleeding.
30- // Type-assert to access webkit-specific background color methods.30+ switch {
31- if wkWV, ok := wv.(*webkit.WebView); ok {31+ case strings.HasPrefix(uri, "dumb://"):
32- switch {32+ // Internal pages: apply themed background
33- case strings.HasPrefix(uri, "dumb://"):33+ theme, ok := c.getCurrentTheme()
34- // Internal pages: apply themed background34+ if ok && theme.prefersDark {
35- theme, ok := c.getCurrentTheme()35+ wv.SetBackgroundColor(darkBgR, darkBgG, darkBgB, darkBgA)
36- if ok && theme.prefersDark {36+ } else {
37- wkWV.SetBackgroundColor(darkBgR, darkBgG, darkBgB, darkBgA)37+ wv.ResetBackgroundToDefault()
38- } else {
39- wkWV.ResetBackgroundToDefault()
40- }
41- case strings.HasPrefix(uri, "about:"):
42- // Keep pool background (no action)
43- default:
44- // External pages: white background
45- wkWV.ResetBackgroundToDefault()
46 }38 }
39+ case strings.HasPrefix(uri, "about:"):
40+ // Keep pool background (no action)
41+ default:
42+ // External pages: white background
43+ wv.ResetBackgroundToDefault()
47 }44 }
48 45
49 // Show the WebView now that content is being painted46 // Show the WebView now that content is being painted
@@ -259,10 +256,12 @@ func (c *Coordinator) revealIfPending(ctx context.Context, paneID entity.PaneID,
259 return256 return
260 }257 }
261 258
262- // Type-assert to access webkit-specific Widget() for visibility control259+ // Use NativeWidgetProvider to make the widget visible (engine-agnostic)
263- if wkWV, ok := wv.(*webkit.WebView); ok {260+ if nwp, ok := wv.(port.NativeWidgetProvider); ok {
264- if inner := wkWV.Widget(); inner != nil {261+ if ptr := nwp.NativeWidget(); ptr != 0 {
265- inner.SetVisible(true)262+ gtkWidget := &gtk.Widget{}
263+ gtkWidget.Ptr = ptr
264+ gtkWidget.SetVisible(true)
266 logging.FromContext(ctx).265 logging.FromContext(ctx).
267 Debug().266 Debug().
268 Str("pane_id", string(paneID)).267 Str("pane_id", string(paneID)).
M internal/ui/coordinator/content/title.go +15 -20
@@ -6,7 +6,6 @@ import (
66
77 "github.com/bnema/dumber/internal/application/port"
88 "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/infrastructure/webkit"
109 "github.com/bnema/dumber/internal/logging"
1110 "github.com/bnema/dumber/internal/ui/adapter"
1211 "github.com/bnema/dumber/internal/ui/layout"
@@ -209,27 +208,23 @@ func (c *Coordinator) resolveCommittedFavicon(ctx context.Context, paneID entity
209208 return
210209 }
211210
212- // Type-assert to access webkit-specific Favicon() and Generation()
213- wkWV, ok := wv.(*webkit.WebView)
214- if !ok {
215- return
216- }
217-
218- // Check if WebKit already has a favicon for this page
219- if icon := wkWV.Favicon(); icon != nil {
220- log.Debug().Str("pane_id", string(paneID)).Str("uri", uri).Msg("using existing WebKit favicon for committed page")
221- // Store and update through the normal path
222- c.navOriginMu.RLock()
223- originURL := c.navOrigins[paneID]
224- c.navOriginMu.RUnlock()
225- c.faviconAdapter.StoreFromWebKitWithOrigin(ctx, uri, originURL, icon)
226- c.updateStackedFaviconForPane(ctx, paneID, icon)
227- return
211+ // Check if the WebView already has a favicon for this page
212+ if icon := wv.Favicon(); icon != nil {
213+ if gdkIcon, ok := icon.(*gdk.Texture); ok {
214+ log.Debug().Str("pane_id", string(paneID)).Str("uri", uri).Msg("using existing favicon for committed page")
215+ // Store and update through the normal path
216+ c.navOriginMu.RLock()
217+ originURL := c.navOrigins[paneID]
218+ c.navOriginMu.RUnlock()
219+ c.faviconAdapter.StoreFromWebKitWithOrigin(ctx, uri, originURL, gdkIcon)
220+ c.updateStackedFaviconForPane(ctx, paneID, gdkIcon)
221+ return
222+ }
228223 }
229224
230- // Fall back to GetOrFetch (checks service cache, WebKit DB, then DuckDuckGo API)
225+ // Fall back to GetOrFetch (checks service cache, engine DB, then DuckDuckGo API)
231226 // Capture current generation to guard against stale callbacks
232- gen := wkWV.Generation()
227+ gen := wv.Generation()
233228 capturedURI := uri
234229 c.faviconAdapter.GetOrFetch(ctx, uri, func(texture *gdk.Texture) {
235230 // Skip nil results — a nil means "couldn't resolve", not "no favicon".
@@ -239,7 +234,7 @@ func (c *Coordinator) resolveCommittedFavicon(ctx context.Context, paneID entity
239234 return
240235 }
241236 // Verify WebView is still bound to pane and hasn't been reused
242- if wkWV.Generation() != gen {
237+ if wv.Generation() != gen {
243238 return
244239 }
245240 currentWV := c.getWebViewLocked(paneID)
@@ -6,7 +6,6 @@ import (
6 6
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/domain/entity"8 "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/infrastructure/webkit"
10 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
11 "github.com/bnema/dumber/internal/ui/adapter"10 "github.com/bnema/dumber/internal/ui/adapter"
12 "github.com/bnema/dumber/internal/ui/layout"11 "github.com/bnema/dumber/internal/ui/layout"
@@ -209,27 +208,23 @@ func (c *Coordinator) resolveCommittedFavicon(ctx context.Context, paneID entity
209 return208 return
210 }209 }
211 210
212- // Type-assert to access webkit-specific Favicon() and Generation()211+ // Check if the WebView already has a favicon for this page
213- wkWV, ok := wv.(*webkit.WebView)212+ if icon := wv.Favicon(); icon != nil {
214- if !ok {213+ if gdkIcon, ok := icon.(*gdk.Texture); ok {
215- return214+ log.Debug().Str("pane_id", string(paneID)).Str("uri", uri).Msg("using existing favicon for committed page")
216- }215+ // Store and update through the normal path
217-216+ c.navOriginMu.RLock()
218- // Check if WebKit already has a favicon for this page217+ originURL := c.navOrigins[paneID]
219- if icon := wkWV.Favicon(); icon != nil {218+ c.navOriginMu.RUnlock()
220- log.Debug().Str("pane_id", string(paneID)).Str("uri", uri).Msg("using existing WebKit favicon for committed page")219+ c.faviconAdapter.StoreFromWebKitWithOrigin(ctx, uri, originURL, gdkIcon)
221- // Store and update through the normal path220+ c.updateStackedFaviconForPane(ctx, paneID, gdkIcon)
222- c.navOriginMu.RLock()221+ return
223- originURL := c.navOrigins[paneID]222+ }
224- c.navOriginMu.RUnlock()
225- c.faviconAdapter.StoreFromWebKitWithOrigin(ctx, uri, originURL, icon)
226- c.updateStackedFaviconForPane(ctx, paneID, icon)
227- return
228 }223 }
229 224
230- // Fall back to GetOrFetch (checks service cache, WebKit DB, then DuckDuckGo API)225+ // Fall back to GetOrFetch (checks service cache, engine DB, then DuckDuckGo API)
231 // Capture current generation to guard against stale callbacks226 // Capture current generation to guard against stale callbacks
232- gen := wkWV.Generation()227+ gen := wv.Generation()
233 capturedURI := uri228 capturedURI := uri
234 c.faviconAdapter.GetOrFetch(ctx, uri, func(texture *gdk.Texture) {229 c.faviconAdapter.GetOrFetch(ctx, uri, func(texture *gdk.Texture) {
235 // Skip nil results — a nil means "couldn't resolve", not "no favicon".230 // Skip nil results — a nil means "couldn't resolve", not "no favicon".
@@ -239,7 +234,7 @@ func (c *Coordinator) resolveCommittedFavicon(ctx context.Context, paneID entity
239 return234 return
240 }235 }
241 // Verify WebView is still bound to pane and hasn't been reused236 // Verify WebView is still bound to pane and hasn't been reused
242- if wkWV.Generation() != gen {237+ if wv.Generation() != gen {
243 return238 return
244 }239 }
245 currentWV := c.getWebViewLocked(paneID)240 currentWV := c.getWebViewLocked(paneID)

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