detached e7434fb

fix(webkit): keep runtime settings updates typed and atomic

commit
e7434fb89f0c1f78e5211e581f8efdaf5a365f3b
author
brice · · 2026-06-23 16:10 UTC
committer
brice · · 2026-06-23 20:01 UTC
parent
9634a01
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
8 files · +161 -5
M internal/application/port/engine.go +1 -0
@@ -162,6 +162,7 @@ type EngineWebContentSettingsPayload struct {
162162 CaptureConsole bool
163163 DrawCompositingIndicators bool
164164 HardwareDecoding EngineHardwareDecodingMode
165+ AutoCopyOnSelection bool
165166 }
166167
167168 // EngineSettingsPayload is the engine-facing boundary view of runtime config.
@@ -162,6 +162,7 @@ type EngineWebContentSettingsPayload struct {
162 CaptureConsole bool162 CaptureConsole bool
163 DrawCompositingIndicators bool163 DrawCompositingIndicators bool
164 HardwareDecoding EngineHardwareDecodingMode164 HardwareDecoding EngineHardwareDecodingMode
165+ AutoCopyOnSelection bool
165 }166 }
166 167
167 // EngineSettingsPayload is the engine-facing boundary view of runtime config.168 // EngineSettingsPayload is the engine-facing boundary view of runtime config.
M internal/application/port/engine_settings_test.go +1 -0
@@ -22,6 +22,7 @@ func TestEngineSettingsPayloadContainsRuntimeWebContentFields(t *testing.T) {
2222 "CaptureConsole",
2323 "DrawCompositingIndicators",
2424 "HardwareDecoding",
25+ "AutoCopyOnSelection",
2526 } {
2627 if _, ok := webContentType.FieldByName(field); !ok {
2728 t.Fatalf("EngineWebContentSettingsPayload missing %s", field)
@@ -22,6 +22,7 @@ func TestEngineSettingsPayloadContainsRuntimeWebContentFields(t *testing.T) {
22 "CaptureConsole",22 "CaptureConsole",
23 "DrawCompositingIndicators",23 "DrawCompositingIndicators",
24 "HardwareDecoding",24 "HardwareDecoding",
25+ "AutoCopyOnSelection",
25 } {26 } {
26 if _, ok := webContentType.FieldByName(field); !ok {27 if _, ok := webContentType.FieldByName(field); !ok {
27 t.Fatalf("EngineWebContentSettingsPayload missing %s", field)28 t.Fatalf("EngineWebContentSettingsPayload missing %s", field)
M internal/bootstrap/runtime_config.go +1 -0
@@ -65,6 +65,7 @@ func EngineSettingsPayloadFromConfig(cfg *config.Config) port.EngineSettingsPayl
6565 CaptureConsole: cfg.Logging.CaptureConsole,
6666 DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,
6767 HardwareDecoding: engineHardwareDecodingModeFromConfig(cfg.Media.HardwareDecodingMode),
68+ AutoCopyOnSelection: cfg.Clipboard.AutoCopyOnSelection,
6869 },
6970 }
7071 }
@@ -65,6 +65,7 @@ func EngineSettingsPayloadFromConfig(cfg *config.Config) port.EngineSettingsPayl
65 CaptureConsole: cfg.Logging.CaptureConsole,65 CaptureConsole: cfg.Logging.CaptureConsole,
66 DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,66 DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,
67 HardwareDecoding: engineHardwareDecodingModeFromConfig(cfg.Media.HardwareDecodingMode),67 HardwareDecoding: engineHardwareDecodingModeFromConfig(cfg.Media.HardwareDecodingMode),
68+ AutoCopyOnSelection: cfg.Clipboard.AutoCopyOnSelection,
68 },69 },
69 }70 }
70 }71 }
M internal/bootstrap/runtime_config_test.go +5 -0
@@ -58,6 +58,7 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) {
5858 cfg.Logging.CaptureConsole = true
5959 cfg.Engine.WebKit.DrawCompositingIndicators = true
6060 cfg.Media.HardwareDecodingMode = config.HardwareDecodingForce
61+ cfg.Clipboard.AutoCopyOnSelection = true
6162
6263 got := EngineSettingsPayloadFromConfig(cfg)
6364
@@ -78,6 +79,10 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) {
7879 if got.WebContent.HardwareDecoding != port.EngineHardwareDecodingForce {
7980 t.Fatalf("HardwareDecoding=%q, want %q", got.WebContent.HardwareDecoding, port.EngineHardwareDecodingForce)
8081 }
82+ autoCopyField := reflect.ValueOf(got.WebContent).FieldByName("AutoCopyOnSelection")
83+ if !autoCopyField.IsValid() || !autoCopyField.Bool() {
84+ t.Fatalf("AutoCopyOnSelection not mapped in web content payload: %#v", got.WebContent)
85+ }
8186 }
8287
8388 func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) {
@@ -58,6 +58,7 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) {
58 cfg.Logging.CaptureConsole = true58 cfg.Logging.CaptureConsole = true
59 cfg.Engine.WebKit.DrawCompositingIndicators = true59 cfg.Engine.WebKit.DrawCompositingIndicators = true
60 cfg.Media.HardwareDecodingMode = config.HardwareDecodingForce60 cfg.Media.HardwareDecodingMode = config.HardwareDecodingForce
61+ cfg.Clipboard.AutoCopyOnSelection = true
61 62
62 got := EngineSettingsPayloadFromConfig(cfg)63 got := EngineSettingsPayloadFromConfig(cfg)
63 64
@@ -78,6 +79,10 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) {
78 if got.WebContent.HardwareDecoding != port.EngineHardwareDecodingForce {79 if got.WebContent.HardwareDecoding != port.EngineHardwareDecodingForce {
79 t.Fatalf("HardwareDecoding=%q, want %q", got.WebContent.HardwareDecoding, port.EngineHardwareDecodingForce)80 t.Fatalf("HardwareDecoding=%q, want %q", got.WebContent.HardwareDecoding, port.EngineHardwareDecodingForce)
80 }81 }
82+ autoCopyField := reflect.ValueOf(got.WebContent).FieldByName("AutoCopyOnSelection")
83+ if !autoCopyField.IsValid() || !autoCopyField.Bool() {
84+ t.Fatalf("AutoCopyOnSelection not mapped in web content payload: %#v", got.WebContent)
85+ }
81 }86 }
82 87
83 func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) {88 func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) {
M internal/infrastructure/webkit/engine_init.go +13 -3
@@ -67,9 +67,7 @@ func NewEngine(
6767 settings := NewSettingsManager(ctx, initialSettings)
6868 injector := NewContentInjector(colorResolver)
6969
70- injector.SetAutoCopyConfigGetter(func() bool {
71- return config.Get().Clipboard.AutoCopyOnSelection
72- })
70+ engineConfigureContentInjectorRuntimeSettings(injector, settings)
7371
7472 prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
7573 themeCSSText := themeManager.GetWebUIThemeCSS()
@@ -130,6 +128,18 @@ func NewEngine(
130128 return engine, nil
131129 }
132130
131+func engineConfigureContentInjectorRuntimeSettings(injector *ContentInjector, settings *SettingsManager) {
132+ if injector == nil {
133+ return
134+ }
135+ injector.SetAutoCopyConfigGetter(func() bool {
136+ if settings == nil {
137+ return false
138+ }
139+ return settings.current().WebContent.AutoCopyOnSelection
140+ })
141+}
142+
133143 // engineSurveyHardwareAndResolveProfile surveys hardware and resolves the performance profile.
134144 func engineSurveyHardwareAndResolveProfile(
135145 ctx context.Context, cfg *config.Config, logger zerolog.Logger,
@@ -67,9 +67,7 @@ func NewEngine(
67 settings := NewSettingsManager(ctx, initialSettings)67 settings := NewSettingsManager(ctx, initialSettings)
68 injector := NewContentInjector(colorResolver)68 injector := NewContentInjector(colorResolver)
69 69
70- injector.SetAutoCopyConfigGetter(func() bool {70+ engineConfigureContentInjectorRuntimeSettings(injector, settings)
71- return config.Get().Clipboard.AutoCopyOnSelection
72- })
73 71
74 prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)72 prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
75 themeCSSText := themeManager.GetWebUIThemeCSS()73 themeCSSText := themeManager.GetWebUIThemeCSS()
@@ -130,6 +128,18 @@ func NewEngine(
130 return engine, nil128 return engine, nil
131 }129 }
132 130
131+func engineConfigureContentInjectorRuntimeSettings(injector *ContentInjector, settings *SettingsManager) {
132+ if injector == nil {
133+ return
134+ }
135+ injector.SetAutoCopyConfigGetter(func() bool {
136+ if settings == nil {
137+ return false
138+ }
139+ return settings.current().WebContent.AutoCopyOnSelection
140+ })
141+}
142+
133 // engineSurveyHardwareAndResolveProfile surveys hardware and resolves the performance profile.143 // engineSurveyHardwareAndResolveProfile surveys hardware and resolves the performance profile.
134 func engineSurveyHardwareAndResolveProfile(144 func engineSurveyHardwareAndResolveProfile(
135 ctx context.Context, cfg *config.Config, logger zerolog.Logger,145 ctx context.Context, cfg *config.Config, logger zerolog.Logger,
M internal/infrastructure/webkit/engine_runtime_test.go +27 -0
@@ -2,6 +2,7 @@ package webkit
22
33 import (
44 "context"
5+ "os"
56 "testing"
67
78 "github.com/bnema/dumber/internal/application/port"
@@ -72,3 +73,29 @@ func TestEngine_Close_NilPool(t *testing.T) {
7273 err := e.Close()
7374 require.NoError(t, err)
7475 }
76+
77+func TestEngineConfigureContentInjectorAutoCopyGetterReadsCurrentPayload(t *testing.T) {
78+ settings := NewSettingsManager(context.Background(), port.EngineSettingsPayload{})
79+ injector := NewContentInjector(nil)
80+
81+ engineConfigureContentInjectorRuntimeSettings(injector, settings)
82+
83+ require.NotNil(t, injector.autoCopyConfigGetter)
84+ require.False(t, injector.autoCopyConfigGetter())
85+
86+ settings.UpdateFromPayload(context.Background(), port.EngineSettingsPayload{
87+ WebContent: port.EngineWebContentSettingsPayload{
88+ AutoCopyOnSelection: true,
89+ },
90+ })
91+
92+ require.True(t, injector.autoCopyConfigGetter())
93+}
94+
95+func TestNewEngineAutoCopyGetterUsesRuntimeSettingsPayload(t *testing.T) {
96+ source, err := os.ReadFile("engine_init.go")
97+ require.NoError(t, err)
98+ globalGetter := "config." + "Get()" + ".Clipboard.AutoCopyOnSelection"
99+ require.NotContains(t, string(source), globalGetter)
100+ require.Contains(t, string(source), "settings.current().WebContent.AutoCopyOnSelection")
101+}
@@ -2,6 +2,7 @@ package webkit
2 2
3 import (3 import (
4 "context"4 "context"
5+ "os"
5 "testing"6 "testing"
6 7
7 "github.com/bnema/dumber/internal/application/port"8 "github.com/bnema/dumber/internal/application/port"
@@ -72,3 +73,29 @@ func TestEngine_Close_NilPool(t *testing.T) {
72 err := e.Close()73 err := e.Close()
73 require.NoError(t, err)74 require.NoError(t, err)
74 }75 }
76+
77+func TestEngineConfigureContentInjectorAutoCopyGetterReadsCurrentPayload(t *testing.T) {
78+ settings := NewSettingsManager(context.Background(), port.EngineSettingsPayload{})
79+ injector := NewContentInjector(nil)
80+
81+ engineConfigureContentInjectorRuntimeSettings(injector, settings)
82+
83+ require.NotNil(t, injector.autoCopyConfigGetter)
84+ require.False(t, injector.autoCopyConfigGetter())
85+
86+ settings.UpdateFromPayload(context.Background(), port.EngineSettingsPayload{
87+ WebContent: port.EngineWebContentSettingsPayload{
88+ AutoCopyOnSelection: true,
89+ },
90+ })
91+
92+ require.True(t, injector.autoCopyConfigGetter())
93+}
94+
95+func TestNewEngineAutoCopyGetterUsesRuntimeSettingsPayload(t *testing.T) {
96+ source, err := os.ReadFile("engine_init.go")
97+ require.NoError(t, err)
98+ globalGetter := "config." + "Get()" + ".Clipboard.AutoCopyOnSelection"
99+ require.NotContains(t, string(source), globalGetter)
100+ require.Contains(t, string(source), "settings.current().WebContent.AutoCopyOnSelection")
101+}
M internal/infrastructure/webkit/settings.go +21 -2
@@ -10,6 +10,21 @@ import (
1010 "github.com/rs/zerolog"
1111 )
1212
13+const hardwareRequiredContentTypes = "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265"
14+
15+type 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+
1328 // SettingsManager creates and manages WebKit Settings instances from payloads.
1429 type SettingsManager struct {
1530 settings port.EngineSettingsPayload
@@ -104,7 +119,7 @@ func applyBrowsingSettings(settings *webkit.Settings) {
104119 settings.SetEnableSiteSpecificQuirks(true)
105120 }
106121
107-func applyMediaSettings(settings *webkit.Settings, mode port.EngineHardwareDecodingMode, log *zerolog.Logger) {
122+func applyMediaSettings(settings mediaSettings, mode port.EngineHardwareDecodingMode, log *zerolog.Logger) {
108123 settings.SetEnableWebaudio(true)
109124 settings.SetEnableWebgl(true)
110125 settings.SetEnableMedia(true)
@@ -116,14 +131,18 @@ func applyMediaSettings(settings *webkit.Settings, mode port.EngineHardwareDecod
116131
117132 switch mode {
118133 case port.EngineHardwareDecodingForce:
119- hwTypes := "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265"
134+ hwTypes := hardwareRequiredContentTypes
135+ settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
120136 settings.SetMediaContentTypesRequiringHardwareSupport(&hwTypes)
121137 log.Debug().Msg("hardware decoding: forced (may fail without hw support)")
122138 case port.EngineHardwareDecodingDisable:
139+ emptyTypes := ""
123140 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)
141+ settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
124142 log.Debug().Msg("hardware decoding: disabled (software only)")
125143 default:
126144 emptyTypes := ""
145+ settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
127146 settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
128147 log.Debug().Msg("hardware decoding: auto (hw preferred, software fallback)")
129148 }
@@ -10,6 +10,21 @@ import (
10 "github.com/rs/zerolog"10 "github.com/rs/zerolog"
11 )11 )
12 12
13+const hardwareRequiredContentTypes = "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265"
14+
15+type 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+
13 // SettingsManager creates and manages WebKit Settings instances from payloads.28 // SettingsManager creates and manages WebKit Settings instances from payloads.
14 type SettingsManager struct {29 type SettingsManager struct {
15 settings port.EngineSettingsPayload30 settings port.EngineSettingsPayload
@@ -104,7 +119,7 @@ func applyBrowsingSettings(settings *webkit.Settings) {
104 settings.SetEnableSiteSpecificQuirks(true)119 settings.SetEnableSiteSpecificQuirks(true)
105 }120 }
106 121
107-func applyMediaSettings(settings *webkit.Settings, mode port.EngineHardwareDecodingMode, log *zerolog.Logger) {122+func applyMediaSettings(settings mediaSettings, mode port.EngineHardwareDecodingMode, log *zerolog.Logger) {
108 settings.SetEnableWebaudio(true)123 settings.SetEnableWebaudio(true)
109 settings.SetEnableWebgl(true)124 settings.SetEnableWebgl(true)
110 settings.SetEnableMedia(true)125 settings.SetEnableMedia(true)
@@ -116,14 +131,18 @@ func applyMediaSettings(settings *webkit.Settings, mode port.EngineHardwareDecod
116 131
117 switch mode {132 switch mode {
118 case port.EngineHardwareDecodingForce:133 case port.EngineHardwareDecodingForce:
119- hwTypes := "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265"134+ hwTypes := hardwareRequiredContentTypes
135+ settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
120 settings.SetMediaContentTypesRequiringHardwareSupport(&hwTypes)136 settings.SetMediaContentTypesRequiringHardwareSupport(&hwTypes)
121 log.Debug().Msg("hardware decoding: forced (may fail without hw support)")137 log.Debug().Msg("hardware decoding: forced (may fail without hw support)")
122 case port.EngineHardwareDecodingDisable:138 case port.EngineHardwareDecodingDisable:
139+ emptyTypes := ""
123 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)140 settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyNeverValue)
141+ settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
124 log.Debug().Msg("hardware decoding: disabled (software only)")142 log.Debug().Msg("hardware decoding: disabled (software only)")
125 default:143 default:
126 emptyTypes := ""144 emptyTypes := ""
145+ settings.SetHardwareAccelerationPolicy(webkit.HardwareAccelerationPolicyAlwaysValue)
127 settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)146 settings.SetMediaContentTypesRequiringHardwareSupport(&emptyTypes)
128 log.Debug().Msg("hardware decoding: auto (hw preferred, software fallback)")147 log.Debug().Msg("hardware decoding: auto (hw preferred, software fallback)")
129 }148 }
M internal/infrastructure/webkit/settings_runtime_test.go +92 -0
@@ -5,6 +5,8 @@ import (
55 "testing"
66
77 "github.com/bnema/dumber/internal/application/port"
8+ "github.com/bnema/puregotk/v4/webkit"
9+ "github.com/rs/zerolog"
810 )
911
1012 func TestSettingsManagerUpdateFromPayloadStoresTypedRuntimeSettings(t *testing.T) {
@@ -30,3 +32,93 @@ func TestSettingsManagerUpdateFromPayloadStoresTypedRuntimeSettings(t *testing.T
3032 t.Fatalf("stored payload=%#v", got)
3133 }
3234 }
35+
36+func TestApplyMediaSettingsUpdatesHardwareDecodingKnobsOnSameSettingsObject(t *testing.T) {
37+ tests := []struct {
38+ name string
39+ first port.EngineHardwareDecodingMode
40+ next port.EngineHardwareDecodingMode
41+ wantPolicy webkit.HardwareAccelerationPolicy
42+ wantHWTypes string
43+ wantEmptyHWT bool
44+ }{
45+ {
46+ name: "disable to force resets acceleration policy",
47+ first: port.EngineHardwareDecodingDisable,
48+ next: port.EngineHardwareDecodingForce,
49+ wantPolicy: webkit.HardwareAccelerationPolicyAlwaysValue,
50+ wantHWTypes: "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265",
51+ },
52+ {
53+ name: "force to disable clears required content types",
54+ first: port.EngineHardwareDecodingForce,
55+ next: port.EngineHardwareDecodingDisable,
56+ wantPolicy: webkit.HardwareAccelerationPolicyNeverValue,
57+ wantEmptyHWT: true,
58+ },
59+ {
60+ name: "disable to auto resets acceleration policy",
61+ first: port.EngineHardwareDecodingDisable,
62+ next: port.EngineHardwareDecodingAuto,
63+ wantPolicy: webkit.HardwareAccelerationPolicyAlwaysValue,
64+ wantEmptyHWT: true,
65+ },
66+ }
67+
68+ logger := zerolog.Nop()
69+ for _, tt := range tests {
70+ t.Run(tt.name, func(t *testing.T) {
71+ settings := &recordingMediaSettings{}
72+
73+ applyMediaSettings(settings, tt.first, &logger)
74+ applyMediaSettings(settings, tt.next, &logger)
75+
76+ if got := settings.hardwareAccelerationPolicy; got != tt.wantPolicy {
77+ t.Fatalf("HardwareAccelerationPolicy=%v, want %v", got, tt.wantPolicy)
78+ }
79+ gotHWTypes := settings.mediaContentTypesRequiringHardwareSupport
80+ if tt.wantEmptyHWT {
81+ if gotHWTypes != "" {
82+ t.Fatalf("MediaContentTypesRequiringHardwareSupport=%q, want empty", gotHWTypes)
83+ }
84+ return
85+ }
86+ if gotHWTypes != tt.wantHWTypes {
87+ t.Fatalf("MediaContentTypesRequiringHardwareSupport=%q, want %q", gotHWTypes, tt.wantHWTypes)
88+ }
89+ })
90+ }
91+}
92+
93+type recordingMediaSettings struct {
94+ hardwareAccelerationPolicy webkit.HardwareAccelerationPolicy
95+ mediaContentTypesRequiringHardwareSupport string
96+}
97+
98+func (*recordingMediaSettings) SetEnableWebaudio(bool) {}
99+
100+func (*recordingMediaSettings) SetEnableWebgl(bool) {}
101+
102+func (*recordingMediaSettings) SetEnableMedia(bool) {}
103+
104+func (*recordingMediaSettings) SetEnableMediasource(bool) {}
105+
106+func (*recordingMediaSettings) SetEnableMediaCapabilities(bool) {}
107+
108+func (*recordingMediaSettings) SetEnableEncryptedMedia(bool) {}
109+
110+func (*recordingMediaSettings) SetMediaPlaybackRequiresUserGesture(bool) {}
111+
112+func (*recordingMediaSettings) SetMediaPlaybackAllowsInline(bool) {}
113+
114+func (s *recordingMediaSettings) SetHardwareAccelerationPolicy(policy webkit.HardwareAccelerationPolicy) {
115+ s.hardwareAccelerationPolicy = policy
116+}
117+
118+func (s *recordingMediaSettings) SetMediaContentTypesRequiringHardwareSupport(contentTypes *string) {
119+ if contentTypes == nil {
120+ s.mediaContentTypesRequiringHardwareSupport = ""
121+ return
122+ }
123+ s.mediaContentTypesRequiringHardwareSupport = *contentTypes
124+}
@@ -5,6 +5,8 @@ import (
5 "testing"5 "testing"
6 6
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8+ "github.com/bnema/puregotk/v4/webkit"
9+ "github.com/rs/zerolog"
8 )10 )
9 11
10 func TestSettingsManagerUpdateFromPayloadStoresTypedRuntimeSettings(t *testing.T) {12 func TestSettingsManagerUpdateFromPayloadStoresTypedRuntimeSettings(t *testing.T) {
@@ -30,3 +32,93 @@ func TestSettingsManagerUpdateFromPayloadStoresTypedRuntimeSettings(t *testing.T
30 t.Fatalf("stored payload=%#v", got)32 t.Fatalf("stored payload=%#v", got)
31 }33 }
32 }34 }
35+
36+func TestApplyMediaSettingsUpdatesHardwareDecodingKnobsOnSameSettingsObject(t *testing.T) {
37+ tests := []struct {
38+ name string
39+ first port.EngineHardwareDecodingMode
40+ next port.EngineHardwareDecodingMode
41+ wantPolicy webkit.HardwareAccelerationPolicy
42+ wantHWTypes string
43+ wantEmptyHWT bool
44+ }{
45+ {
46+ name: "disable to force resets acceleration policy",
47+ first: port.EngineHardwareDecodingDisable,
48+ next: port.EngineHardwareDecodingForce,
49+ wantPolicy: webkit.HardwareAccelerationPolicyAlwaysValue,
50+ wantHWTypes: "video/av01;video/mp4;video/webm;video/x-h264;video/x-h265",
51+ },
52+ {
53+ name: "force to disable clears required content types",
54+ first: port.EngineHardwareDecodingForce,
55+ next: port.EngineHardwareDecodingDisable,
56+ wantPolicy: webkit.HardwareAccelerationPolicyNeverValue,
57+ wantEmptyHWT: true,
58+ },
59+ {
60+ name: "disable to auto resets acceleration policy",
61+ first: port.EngineHardwareDecodingDisable,
62+ next: port.EngineHardwareDecodingAuto,
63+ wantPolicy: webkit.HardwareAccelerationPolicyAlwaysValue,
64+ wantEmptyHWT: true,
65+ },
66+ }
67+
68+ logger := zerolog.Nop()
69+ for _, tt := range tests {
70+ t.Run(tt.name, func(t *testing.T) {
71+ settings := &recordingMediaSettings{}
72+
73+ applyMediaSettings(settings, tt.first, &logger)
74+ applyMediaSettings(settings, tt.next, &logger)
75+
76+ if got := settings.hardwareAccelerationPolicy; got != tt.wantPolicy {
77+ t.Fatalf("HardwareAccelerationPolicy=%v, want %v", got, tt.wantPolicy)
78+ }
79+ gotHWTypes := settings.mediaContentTypesRequiringHardwareSupport
80+ if tt.wantEmptyHWT {
81+ if gotHWTypes != "" {
82+ t.Fatalf("MediaContentTypesRequiringHardwareSupport=%q, want empty", gotHWTypes)
83+ }
84+ return
85+ }
86+ if gotHWTypes != tt.wantHWTypes {
87+ t.Fatalf("MediaContentTypesRequiringHardwareSupport=%q, want %q", gotHWTypes, tt.wantHWTypes)
88+ }
89+ })
90+ }
91+}
92+
93+type recordingMediaSettings struct {
94+ hardwareAccelerationPolicy webkit.HardwareAccelerationPolicy
95+ mediaContentTypesRequiringHardwareSupport string
96+}
97+
98+func (*recordingMediaSettings) SetEnableWebaudio(bool) {}
99+
100+func (*recordingMediaSettings) SetEnableWebgl(bool) {}
101+
102+func (*recordingMediaSettings) SetEnableMedia(bool) {}
103+
104+func (*recordingMediaSettings) SetEnableMediasource(bool) {}
105+
106+func (*recordingMediaSettings) SetEnableMediaCapabilities(bool) {}
107+
108+func (*recordingMediaSettings) SetEnableEncryptedMedia(bool) {}
109+
110+func (*recordingMediaSettings) SetMediaPlaybackRequiresUserGesture(bool) {}
111+
112+func (*recordingMediaSettings) SetMediaPlaybackAllowsInline(bool) {}
113+
114+func (s *recordingMediaSettings) SetHardwareAccelerationPolicy(policy webkit.HardwareAccelerationPolicy) {
115+ s.hardwareAccelerationPolicy = policy
116+}
117+
118+func (s *recordingMediaSettings) SetMediaContentTypesRequiringHardwareSupport(contentTypes *string) {
119+ if contentTypes == nil {
120+ s.mediaContentTypesRequiringHardwareSupport = ""
121+ return
122+ }
123+ s.mediaContentTypesRequiringHardwareSupport = *contentTypes
124+}

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