fix(cef): fail unsupported engine contracts explicitly
- commit
- 52227071d659c42021c7bc2f770d10388035555a
- author
- brice · · 2026-04-11 12:40 UTC
- parent
- 7df3367
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 | |||
| 225 | 225 | ||
| 226 | 226 | // ConfigureDownloads sets up download handling (Phase 1 stub). | |
| 227 | 227 | func (e *Engine) ConfigureDownloads(_ context.Context, _ string, _ port.DownloadEventHandler, _ port.DownloadPreparer) error { | |
| 228 | - | return nil | |
| 228 | + | return ErrDownloadsUnsupported | |
| 229 | 229 | } | |
| 230 | 230 | ||
| 231 | 231 | // 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 nil | 228 | + | 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( | |||
| 212 | 212 | } | |
| 213 | 213 | } | |
| 214 | 214 | ||
| 215 | - | // createRelatedWarned ensures the session sharing warning is logged only once. | |
| 216 | - | var createRelatedWarned atomic.Bool | |
| 217 | - | ||
| 218 | 215 | // CreateRelated creates a WebView that shares session/cookies with the parent. | |
| 219 | 216 | // TODO(phase2): look up the parent browser by parentID and use the same | |
| 220 | 217 | // request context so cookies and session state are shared. | |
| 221 | 218 | 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 | |
| 226 | 220 | } | |
| @@ -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 same | 216 | // 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 | + | } | |||