settings.go RawHistoryFolder
1package webkit
2
3import (
4 "context"
5 "sync"
6
7 "github.com/bnema/dumber/internal/domain/entity"
8 "github.com/bnema/dumber/internal/logging"
9 "github.com/bnema/puregotk/v4/webkit"
10 "github.com/rs/zerolog"
11)
12
13const hardwareRequiredContentTypes = "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265"
14
15type mediaSettings interface {
16 SetEnableWebaudio(bool)
17 SetEnableWebgl(bool)
18 SetEnableMedia(bool)
19 SetEnableMediasource(bool)
20 SetEnableMediaCapabilities(bool)
21 SetEnableEncryptedMedia(bool)
22 SetMediaPlaybackRequiresUserGesture(bool)
23 SetMediaPlaybackAllowsInline(bool)
24 SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicy)
25 SetMediaContentTypesRequiringHardwareSupport(*string)
26}
27
28// SettingsManager creates and manages WebKit Settings instances from payloads.
29type SettingsManager struct {
30 settings entity.EngineSettingsPayload
31 mu sync.RWMutex
32}
33
34// NewSettingsManager creates a new SettingsManager with the given payload.
35func NewSettingsManager(ctx context.Context, settings entity.EngineSettingsPayload) *SettingsManager {
36 log := logging.FromContext(ctx)
37 log.Debug().Msg("creating settings manager")
38 return &SettingsManager{settings: settings}
39}
40
41func (sm *SettingsManager) current() entity.EngineSettingsPayload {
42 sm.mu.RLock()
43 defer sm.mu.RUnlock()
44 return sm.settings
45}
46
47// CreateSettings creates a new webkit.Settings instance configured from the current payload.
48func (sm *SettingsManager) CreateSettings(ctx context.Context) *webkit.Settings {
49 log := logging.FromContext(ctx)
50 payload := sm.current()
51
52 settings := webkit.NewSettings()
53 if settings == nil {
54 log.Error().Msg("failed to create webkit settings")
55 return nil
56 }
57
58 sm.applySettings(ctx, settings, payload)
59 return settings
60}
61
62// applySettings applies settings payload to a webkit.Settings instance.
63func (sm *SettingsManager) applySettings(ctx context.Context, settings *webkit.Settings, payload entity.EngineSettingsPayload) {
64 if sm == nil {
65 return
66 }
67 log := logging.FromContext(ctx)
68 applyJavaScriptSettings(settings)
69 applyFontSettings(settings, payload.WebContent)
70 applyDebugSettings(settings, payload.WebContent)
71 applyBrowsingSettings(settings)
72 applyMediaSettings(settings, payload.WebContent.HardwareDecoding, log)
73 applyStorageSettings(settings)
74 applyUISettings(settings)
75 applyCanvasSettings(settings)
76 applyWebRTCSettings(settings)
77
78 webrtcEnabled := settings.GetPropertyEnableWebrtc()
79 mediaStreamEnabled := settings.GetPropertyEnableMediaStream()
80
81 log.Debug().
82 Str("sans_font", payload.WebContent.SansFont).
83 Bool("developer_extras", payload.WebContent.EnableDevTools).
84 Bool("webrtc_enabled", webrtcEnabled).
85 Bool("media_stream_enabled", mediaStreamEnabled).
86 Msg("settings applied")
87}
88
89func applyJavaScriptSettings(settings *webkit.Settings) {
90 settings.SetEnableJavascript(true)
91 settings.SetEnableJavascriptMarkup(true)
92}
93
94func applyFontSettings(settings *webkit.Settings, payload entity.EngineWebContentSettingsPayload) {
95 if payload.SansFont != "" {
96 settings.SetDefaultFontFamily(payload.SansFont)
97 settings.SetSansSerifFontFamily(payload.SansFont)
98 }
99 if payload.SerifFont != "" {
100 settings.SetSerifFontFamily(payload.SerifFont)
101 }
102 if payload.MonospaceFont != "" {
103 settings.SetMonospaceFontFamily(payload.MonospaceFont)
104 }
105 if payload.DefaultFontSize > 0 {
106 settings.SetDefaultFontSize(uint32(payload.DefaultFontSize))
107 }
108}
109
110func applyDebugSettings(settings *webkit.Settings, payload entity.EngineWebContentSettingsPayload) {
111 settings.SetEnableDeveloperExtras(payload.EnableDevTools)
112 settings.SetEnableWriteConsoleMessagesToStdout(payload.CaptureConsole)
113 settings.SetDrawCompositingIndicators(payload.DrawCompositingIndicators)
114}
115
116func applyBrowsingSettings(settings *webkit.Settings) {
117 settings.SetEnableSmoothScrolling(true)
118 settings.SetEnablePageCache(true)
119 settings.SetEnableSiteSpecificQuirks(true)
120}
121
122func applyMediaSettings(settings mediaSettings, mode entity.EngineHardwareDecodingMode, log *zerolog.Logger) {
123 settings.SetEnableWebaudio(true)
124 settings.SetEnableWebgl(true)
125 settings.SetEnableMedia(true)
126 settings.SetEnableMediasource(true)
127 settings.SetEnableMediaCapabilities(true)
128 settings.SetEnableEncryptedMedia(true)
129 settings.SetMediaPlaybackRequiresUserGesture(true)
130 settings.SetMediaPlaybackAllowsInline(true)
131
132 switch mode {
133 case entity.EngineHardwareDecodingForce:
134 hwTypes := hardwareRequiredContentTypes
135 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
136 settings.SetMediaContentTypesRequiringHardwareSupport(&hwTypes)
137 log.Debug().Msg("hardware decoding: forced (may fail without hw support)")
138 case entity.EngineHardwareDecodingDisable:
139 emptyTypes := ""
140 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)
141 settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
142 log.Debug().Msg("hardware decoding: disabled (software only)")
143 default:
144 emptyTypes := ""
145 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
146 settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
147 log.Debug().Msg("hardware decoding: auto (hw preferred, software fallback)")
148 }
149}
150
151func applyStorageSettings(settings *webkit.Settings) {
152 settings.SetEnableHtml5LocalStorage(true)
153 settings.SetEnableHtml5Database(true)
154}
155
156func applyUISettings(settings *webkit.Settings) {
157 settings.SetEnableBackForwardNavigationGestures(true)
158 settings.SetEnableFullscreen(true)
159}
160
161func applyCanvasSettings(settings *webkit.Settings) {
162 settings.SetEnable2dCanvasAcceleration(true)
163}
164
165func applyWebRTCSettings(settings *webkit.Settings) {
166 // Set both direct API and gobject properties for robustness with generated bindings.
167 settings.SetPropertyEnableMediaStream(true)
168 settings.SetPropertyEnableWebrtc(true)
169
170 settings.SetEnableMediaStream(true)
171 settings.SetEnableWebrtc(true)
172}
173
174// UpdateFromPayload updates the manager with a new payload (for hot-reload).
175// Note: This doesn't update already-created Settings instances.
176// New WebViews will use the updated payload.
177func (sm *SettingsManager) UpdateFromPayload(ctx context.Context, settings entity.EngineSettingsPayload) {
178 log := logging.FromContext(ctx)
179 sm.mu.Lock()
180 defer sm.mu.Unlock()
181 sm.settings = settings
182 log.Debug().Msg("settings payload updated")
183}
184
185// ApplyToWebView applies current settings to an existing WebView.
186// This can be used to update a WebView's settings after config hot-reload.
187func (sm *SettingsManager) ApplyToWebView(ctx context.Context, wv *webkit.WebView) {
188 if sm == nil {
189 return
190 }
191 if wv == nil {
192 return
193 }
194
195 // Apply to the existing settings object attached to the WebView
196 existingSettings := wv.GetSettings()
197 if existingSettings != nil {
198 payload := sm.current()
199 sm.applySettings(ctx, existingSettings, payload)
200 }
201}