detached 5222707

fix(cef): fail unsupported engine contracts explicitly

commit
52227071d659c42021c7bc2f770d10388035555a
author
brice · · 2026-04-11 12:40 UTC
parent
7df3367
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +98 -8
M internal/infrastructure/cef/engine.go +1 -1
@@ -225,7 +225,7 @@ func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler port.Accent
225225
226226 // ConfigureDownloads sets up download handling (Phase 1 stub).
227227 func (e *Engine) ConfigureDownloads(_ context.Context, _ string, _ port.DownloadEventHandler, _ port.DownloadPreparer) error {
228- return nil
228+ return ErrDownloadsUnsupported
229229 }
230230
231231 // OnToolkitReady is called after the UI toolkit has initialized.
@@ -225,7 +225,7 @@ func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler port.Accent
225 225
226 // ConfigureDownloads sets up download handling (Phase 1 stub).226 // ConfigureDownloads sets up download handling (Phase 1 stub).
227 func (e *Engine) ConfigureDownloads(_ context.Context, _ string, _ port.DownloadEventHandler, _ port.DownloadPreparer) error {227 func (e *Engine) ConfigureDownloads(_ context.Context, _ string, _ port.DownloadEventHandler, _ port.DownloadPreparer) error {
228- return nil228+ return ErrDownloadsUnsupported
229 }229 }
230 230
231 // OnToolkitReady is called after the UI toolkit has initialized.231 // OnToolkitReady is called after the UI toolkit has initialized.
A internal/infrastructure/cef/engine_contract_test.go +22 -0
@@ -0,0 +1,22 @@
1+package cef
2+
3+import (
4+ "context"
5+ "testing"
6+
7+ "github.com/bnema/dumber/internal/application/port"
8+ "github.com/stretchr/testify/require"
9+)
10+
11+func TestEngineConfigureDownloads_ReturnsUnsupported(t *testing.T) {
12+ eng := &Engine{}
13+ err := eng.ConfigureDownloads(context.Background(), "/tmp", nil, nil)
14+ require.ErrorIs(t, err, ErrDownloadsUnsupported)
15+}
16+
17+func TestWebViewFactoryCreateRelated_ReturnsUnsupported(t *testing.T) {
18+ factory := &WebViewFactory{}
19+ wv, err := factory.CreateRelated(context.Background(), port.WebViewID(42))
20+ require.Nil(t, wv)
21+ require.ErrorIs(t, err, ErrRelatedWebViewUnsupported)
22+}
@@ -0,0 +1,22 @@
1+package cef
2+
3+import (
4+ "context"
5+ "testing"
6+
7+ "github.com/bnema/dumber/internal/application/port"
8+ "github.com/stretchr/testify/require"
9+)
10+
11+func TestEngineConfigureDownloads_ReturnsUnsupported(t *testing.T) {
12+ eng := &Engine{}
13+ err := eng.ConfigureDownloads(context.Background(), "/tmp", nil, nil)
14+ require.ErrorIs(t, err, ErrDownloadsUnsupported)
15+}
16+
17+func TestWebViewFactoryCreateRelated_ReturnsUnsupported(t *testing.T) {
18+ factory := &WebViewFactory{}
19+ wv, err := factory.CreateRelated(context.Background(), port.WebViewID(42))
20+ require.Nil(t, wv)
21+ require.ErrorIs(t, err, ErrRelatedWebViewUnsupported)
22+}
M internal/infrastructure/cef/factory.go +1 -7
@@ -212,15 +212,9 @@ func (f *WebViewFactory) configureInitialBrowserCreation(
212212 }
213213 }
214214
215-// createRelatedWarned ensures the session sharing warning is logged only once.
216-var createRelatedWarned atomic.Bool
217-
218215 // CreateRelated creates a WebView that shares session/cookies with the parent.
219216 // TODO(phase2): look up the parent browser by parentID and use the same
220217 // request context so cookies and session state are shared.
221218 func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) {
222- if createRelatedWarned.CompareAndSwap(false, true) {
223- logging.FromContext(ctx).Warn().Msg("cef: CreateRelated does not yet support session sharing, creating independent WebView")
224- }
225- return f.Create(ctx)
219+ return nil, ErrRelatedWebViewUnsupported
226220 }
@@ -212,15 +212,9 @@ func (f *WebViewFactory) configureInitialBrowserCreation(
212 }212 }
213 }213 }
214 214
215-// createRelatedWarned ensures the session sharing warning is logged only once.
216-var createRelatedWarned atomic.Bool
217-
218 // CreateRelated creates a WebView that shares session/cookies with the parent.215 // CreateRelated creates a WebView that shares session/cookies with the parent.
219 // TODO(phase2): look up the parent browser by parentID and use the same216 // TODO(phase2): look up the parent browser by parentID and use the same
220 // request context so cookies and session state are shared.217 // request context so cookies and session state are shared.
221 func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) {218 func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) {
222- if createRelatedWarned.CompareAndSwap(false, true) {219+ return nil, ErrRelatedWebViewUnsupported
223- logging.FromContext(ctx).Warn().Msg("cef: CreateRelated does not yet support session sharing, creating independent WebView")
224- }
225- return f.Create(ctx)
226 }220 }
A internal/infrastructure/cef/runtime_config.go +74 -0
@@ -0,0 +1,74 @@
1+package cef
2+
3+import (
4+ "context"
5+ "errors"
6+
7+ "github.com/bnema/dumber/internal/application/port"
8+)
9+
10+var (
11+ ErrDownloadsUnsupported = errors.New("cef: downloads are not supported yet")
12+ ErrRelatedWebViewUnsupported = errors.New("cef: related popup webviews are not supported yet")
13+ ErrCookiePolicyUnsupported = errors.New("cef: non-default cookie policy is not supported yet")
14+)
15+
16+type RuntimeConfig struct {
17+ CEFDir string
18+ LogFile string
19+ LogSeverity int32
20+ WindowlessFrameRate int32
21+ EnableAudioHandler bool
22+ EnableContextMenuHandler bool
23+ TraceHandlers bool
24+}
25+
26+type TranscodingRuntimeConfig struct {
27+ Enabled bool
28+ HWAccel string
29+ MaxConcurrent int
30+ Quality string
31+}
32+
33+type HandlerRegistrar func(context.Context, port.WebUIHandlerRouter, port.HandlerDependencies) error
34+
35+type AccentHandlerRegistrar func(context.Context, port.WebUIHandlerRouter, port.AccentKeyHandler) error
36+
37+type MediaClassifier struct {
38+ IsProprietaryVideoMIME func(string) bool
39+ IsOpenVideoMIME func(string) bool
40+ IsStreamingManifestMIME func(string) bool
41+ IsStreamingManifestURL func(string) bool
42+ IsEagerTranscodeURL func(string) bool
43+ ParseSyntheticTranscodeURL func(string) (string, string, string, bool)
44+}
45+
46+func (m MediaClassifier) normalize() MediaClassifier {
47+ if m.IsProprietaryVideoMIME == nil {
48+ m.IsProprietaryVideoMIME = func(string) bool { return false }
49+ }
50+ if m.IsOpenVideoMIME == nil {
51+ m.IsOpenVideoMIME = func(string) bool { return false }
52+ }
53+ if m.IsStreamingManifestMIME == nil {
54+ m.IsStreamingManifestMIME = func(string) bool { return false }
55+ }
56+ if m.IsStreamingManifestURL == nil {
57+ m.IsStreamingManifestURL = func(string) bool { return false }
58+ }
59+ if m.IsEagerTranscodeURL == nil {
60+ m.IsEagerTranscodeURL = func(string) bool { return false }
61+ }
62+ if m.ParseSyntheticTranscodeURL == nil {
63+ m.ParseSyntheticTranscodeURL = func(string) (string, string, string, bool) { return "", "", "", false }
64+ }
65+ return m
66+}
67+
68+type EngineDependencies struct {
69+ RegisterHandlers HandlerRegistrar
70+ RegisterAccentHandlers AccentHandlerRegistrar
71+ CurrentConfigPayload func() ([]byte, error)
72+ DefaultConfigPayload func() ([]byte, error)
73+ MediaClassifier MediaClassifier
74+}
@@ -0,0 +1,74 @@
1+package cef
2+
3+import (
4+ "context"
5+ "errors"
6+
7+ "github.com/bnema/dumber/internal/application/port"
8+)
9+
10+var (
11+ ErrDownloadsUnsupported = errors.New("cef: downloads are not supported yet")
12+ ErrRelatedWebViewUnsupported = errors.New("cef: related popup webviews are not supported yet")
13+ ErrCookiePolicyUnsupported = errors.New("cef: non-default cookie policy is not supported yet")
14+)
15+
16+type RuntimeConfig struct {
17+ CEFDir string
18+ LogFile string
19+ LogSeverity int32
20+ WindowlessFrameRate int32
21+ EnableAudioHandler bool
22+ EnableContextMenuHandler bool
23+ TraceHandlers bool
24+}
25+
26+type TranscodingRuntimeConfig struct {
27+ Enabled bool
28+ HWAccel string
29+ MaxConcurrent int
30+ Quality string
31+}
32+
33+type HandlerRegistrar func(context.Context, port.WebUIHandlerRouter, port.HandlerDependencies) error
34+
35+type AccentHandlerRegistrar func(context.Context, port.WebUIHandlerRouter, port.AccentKeyHandler) error
36+
37+type MediaClassifier struct {
38+ IsProprietaryVideoMIME func(string) bool
39+ IsOpenVideoMIME func(string) bool
40+ IsStreamingManifestMIME func(string) bool
41+ IsStreamingManifestURL func(string) bool
42+ IsEagerTranscodeURL func(string) bool
43+ ParseSyntheticTranscodeURL func(string) (string, string, string, bool)
44+}
45+
46+func (m MediaClassifier) normalize() MediaClassifier {
47+ if m.IsProprietaryVideoMIME == nil {
48+ m.IsProprietaryVideoMIME = func(string) bool { return false }
49+ }
50+ if m.IsOpenVideoMIME == nil {
51+ m.IsOpenVideoMIME = func(string) bool { return false }
52+ }
53+ if m.IsStreamingManifestMIME == nil {
54+ m.IsStreamingManifestMIME = func(string) bool { return false }
55+ }
56+ if m.IsStreamingManifestURL == nil {
57+ m.IsStreamingManifestURL = func(string) bool { return false }
58+ }
59+ if m.IsEagerTranscodeURL == nil {
60+ m.IsEagerTranscodeURL = func(string) bool { return false }
61+ }
62+ if m.ParseSyntheticTranscodeURL == nil {
63+ m.ParseSyntheticTranscodeURL = func(string) (string, string, string, bool) { return "", "", "", false }
64+ }
65+ return m
66+}
67+
68+type EngineDependencies struct {
69+ RegisterHandlers HandlerRegistrar
70+ RegisterAccentHandlers AccentHandlerRegistrar
71+ CurrentConfigPayload func() ([]byte, error)
72+ DefaultConfigPayload func() ([]byte, error)
73+ MediaClassifier MediaClassifier
74+}

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