feat(webkit): add Engine struct with port.Engine implementation and adapters
Introduces Engine struct wrapping all webkit infrastructure types and implementing port.Engine. Adds engine_adapters.go with adapter types bridging internal webkit types to port interfaces.
- commit
- f7e55189ecd5d6c2fb63983c484e4194a93650c6
- author
- brice · · 2026-03-15 16:20 UTC
- parent
- f6521de
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +244 -0
A internal/infrastructure/webkit/engine.go +85 -0
| @@ -0,0 +1,85 @@ | |||
| 1 | + | package webkit | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/bnema/dumber/internal/infrastructure/filtering" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | // Engine implements port.Engine for the WebKit browser engine. | |
| 11 | + | type Engine struct { | |
| 12 | + | wkCtx *WebKitContext | |
| 13 | + | settings *SettingsManager | |
| 14 | + | injector *ContentInjector | |
| 15 | + | messageRouter *MessageRouter | |
| 16 | + | pool *WebViewPool | |
| 17 | + | factory *WebViewFactory | |
| 18 | + | filterManager *filtering.Manager | |
| 19 | + | schemeHandler *DumbSchemeHandler | |
| 20 | + | schemePath string | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | // Compile-time check that Engine implements port.Engine. | |
| 24 | + | var _ port.Engine = (*Engine)(nil) | |
| 25 | + | ||
| 26 | + | // Init is a no-op for WebKit — initialization happens in NewEngine. | |
| 27 | + | func (e *Engine) Init(_ context.Context, _ port.EngineOptions) error { | |
| 28 | + | return nil | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | // Factory returns the WebViewFactory wrapped as a port.WebViewFactory. | |
| 32 | + | func (e *Engine) Factory() port.WebViewFactory { | |
| 33 | + | return &webViewFactoryAdapter{factory: e.factory} | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | // Pool returns the WebViewPool wrapped as a port.WebViewPool. | |
| 37 | + | func (e *Engine) Pool() port.WebViewPool { | |
| 38 | + | return &webViewPoolAdapter{pool: e.pool} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // ContentInjector returns the ContentInjector implementing port.ContentInjector. | |
| 42 | + | func (e *Engine) ContentInjector() port.ContentInjector { | |
| 43 | + | return e.injector | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // InternalSchemePath returns the URI scheme used for internal app resources. | |
| 47 | + | func (e *Engine) InternalSchemePath() string { | |
| 48 | + | return e.schemePath | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // Close releases all resources held by the engine. | |
| 52 | + | func (e *Engine) Close() error { | |
| 53 | + | if e.pool != nil { | |
| 54 | + | e.pool.Close(context.Background()) | |
| 55 | + | } | |
| 56 | + | return nil | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | // SchemeHandler returns a port.SchemeHandler adapter for the DumbSchemeHandler. | |
| 60 | + | func (e *Engine) SchemeHandler() port.SchemeHandler { | |
| 61 | + | return &schemeHandlerAdapter{handler: e.schemeHandler} | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | // MessageRouter returns a port.MessageRouter adapter for the internal MessageRouter. | |
| 65 | + | func (e *Engine) MessageRouter() port.MessageRouter { | |
| 66 | + | return &messageRouterAdapter{router: e.messageRouter} | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // SettingsApplier returns a port.SettingsApplier adapter for the SettingsManager. | |
| 70 | + | func (e *Engine) SettingsApplier() port.SettingsApplier { | |
| 71 | + | return &settingsApplierAdapter{settings: e.settings} | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // FilterApplier returns a port.FilterApplier adapter, or nil if filtering is disabled. | |
| 75 | + | func (e *Engine) FilterApplier() port.FilterApplier { | |
| 76 | + | if e.filterManager == nil { | |
| 77 | + | return nil | |
| 78 | + | } | |
| 79 | + | return &filterApplierAdapter{manager: e.filterManager} | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | // FaviconDatabase returns a port.FaviconDatabase adapter for async favicon lookups. | |
| 83 | + | func (e *Engine) FaviconDatabase() port.FaviconDatabase { | |
| 84 | + | return &faviconDatabaseAdapter{wkCtx: e.wkCtx} | |
| 85 | + | } | |
| @@ -0,0 +1,85 @@ | |||||
| 1 | + | package webkit | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 7 | + | "github.com/bnema/dumber/internal/infrastructure/filtering" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | // Engine implements port.Engine for the WebKit browser engine. | |||
| 11 | + | type Engine struct { | |||
| 12 | + | wkCtx *WebKitContext | |||
| 13 | + | settings *SettingsManager | |||
| 14 | + | injector *ContentInjector | |||
| 15 | + | messageRouter *MessageRouter | |||
| 16 | + | pool *WebViewPool | |||
| 17 | + | factory *WebViewFactory | |||
| 18 | + | filterManager *filtering.Manager | |||
| 19 | + | schemeHandler *DumbSchemeHandler | |||
| 20 | + | schemePath string | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | // Compile-time check that Engine implements port.Engine. | |||
| 24 | + | var _ port.Engine = (*Engine)(nil) | |||
| 25 | + | ||||
| 26 | + | // Init is a no-op for WebKit — initialization happens in NewEngine. | |||
| 27 | + | func (e *Engine) Init(_ context.Context, _ port.EngineOptions) error { | |||
| 28 | + | return nil | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | // Factory returns the WebViewFactory wrapped as a port.WebViewFactory. | |||
| 32 | + | func (e *Engine) Factory() port.WebViewFactory { | |||
| 33 | + | return &webViewFactoryAdapter{factory: e.factory} | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | // Pool returns the WebViewPool wrapped as a port.WebViewPool. | |||
| 37 | + | func (e *Engine) Pool() port.WebViewPool { | |||
| 38 | + | return &webViewPoolAdapter{pool: e.pool} | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | // ContentInjector returns the ContentInjector implementing port.ContentInjector. | |||
| 42 | + | func (e *Engine) ContentInjector() port.ContentInjector { | |||
| 43 | + | return e.injector | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | // InternalSchemePath returns the URI scheme used for internal app resources. | |||
| 47 | + | func (e *Engine) InternalSchemePath() string { | |||
| 48 | + | return e.schemePath | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | // Close releases all resources held by the engine. | |||
| 52 | + | func (e *Engine) Close() error { | |||
| 53 | + | if e.pool != nil { | |||
| 54 | + | e.pool.Close(context.Background()) | |||
| 55 | + | } | |||
| 56 | + | return nil | |||
| 57 | + | } | |||
| 58 | + | ||||
| 59 | + | // SchemeHandler returns a port.SchemeHandler adapter for the DumbSchemeHandler. | |||
| 60 | + | func (e *Engine) SchemeHandler() port.SchemeHandler { | |||
| 61 | + | return &schemeHandlerAdapter{handler: e.schemeHandler} | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | // MessageRouter returns a port.MessageRouter adapter for the internal MessageRouter. | |||
| 65 | + | func (e *Engine) MessageRouter() port.MessageRouter { | |||
| 66 | + | return &messageRouterAdapter{router: e.messageRouter} | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // SettingsApplier returns a port.SettingsApplier adapter for the SettingsManager. | |||
| 70 | + | func (e *Engine) SettingsApplier() port.SettingsApplier { | |||
| 71 | + | return &settingsApplierAdapter{settings: e.settings} | |||
| 72 | + | } | |||
| 73 | + | ||||
| 74 | + | // FilterApplier returns a port.FilterApplier adapter, or nil if filtering is disabled. | |||
| 75 | + | func (e *Engine) FilterApplier() port.FilterApplier { | |||
| 76 | + | if e.filterManager == nil { | |||
| 77 | + | return nil | |||
| 78 | + | } | |||
| 79 | + | return &filterApplierAdapter{manager: e.filterManager} | |||
| 80 | + | } | |||
| 81 | + | ||||
| 82 | + | // FaviconDatabase returns a port.FaviconDatabase adapter for async favicon lookups. | |||
| 83 | + | func (e *Engine) FaviconDatabase() port.FaviconDatabase { | |||
| 84 | + | return &faviconDatabaseAdapter{wkCtx: e.wkCtx} | |||
| 85 | + | } | |||
A internal/infrastructure/webkit/engine_adapters.go +159 -0
| @@ -0,0 +1,159 @@ | |||
| 1 | + | package webkit | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "fmt" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/dumber/internal/application/port" | |
| 8 | + | "github.com/bnema/dumber/internal/infrastructure/filtering" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // --- WebViewFactory adapter --- | |
| 12 | + | ||
| 13 | + | // webViewFactoryAdapter bridges *WebViewFactory to port.WebViewFactory. | |
| 14 | + | // WebViewFactory.Create and CreateRelated return *WebView, not port.WebView, | |
| 15 | + | // so this adapter wraps the return values. | |
| 16 | + | type webViewFactoryAdapter struct { | |
| 17 | + | factory *WebViewFactory | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | func (a *webViewFactoryAdapter) Create(ctx context.Context) (port.WebView, error) { | |
| 21 | + | wv, err := a.factory.Create(ctx) | |
| 22 | + | if err != nil { | |
| 23 | + | return nil, err | |
| 24 | + | } | |
| 25 | + | return wv, nil | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | func (a *webViewFactoryAdapter) CreateRelated(ctx context.Context, parentID port.WebViewID) (port.WebView, error) { | |
| 29 | + | wv, err := a.factory.CreateRelated(ctx, parentID) | |
| 30 | + | if err != nil { | |
| 31 | + | return nil, err | |
| 32 | + | } | |
| 33 | + | return wv, nil | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | // --- WebViewPool adapter --- | |
| 37 | + | ||
| 38 | + | // webViewPoolAdapter bridges *WebViewPool to port.WebViewPool. | |
| 39 | + | // WebViewPool methods use *WebView and require a context; this adapter adapts | |
| 40 | + | // the signatures to match the port interface. | |
| 41 | + | type webViewPoolAdapter struct { | |
| 42 | + | pool *WebViewPool | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | func (a *webViewPoolAdapter) Acquire(ctx context.Context) (port.WebView, error) { | |
| 46 | + | wv, err := a.pool.Acquire(ctx) | |
| 47 | + | if err != nil { | |
| 48 | + | return nil, err | |
| 49 | + | } | |
| 50 | + | return wv, nil | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | func (a *webViewPoolAdapter) Release(wv port.WebView) { | |
| 54 | + | if wv == nil { | |
| 55 | + | return | |
| 56 | + | } | |
| 57 | + | if wwv, ok := wv.(*WebView); ok { | |
| 58 | + | a.pool.Release(context.Background(), wwv) | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | func (a *webViewPoolAdapter) Prewarm(count int) { | |
| 63 | + | a.pool.Prewarm(context.Background(), count) | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | func (a *webViewPoolAdapter) Size() int { | |
| 67 | + | return a.pool.Size() | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func (a *webViewPoolAdapter) Close() { | |
| 71 | + | a.pool.Close(context.Background()) | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // --- SchemeHandler adapter --- | |
| 75 | + | ||
| 76 | + | // schemeHandlerAdapter bridges *DumbSchemeHandler to port.SchemeHandler. | |
| 77 | + | // DumbSchemeHandler uses a page-based registration pattern (RegisterPage) rather | |
| 78 | + | // than a generic scheme+handler registration; this adapter is a stub until | |
| 79 | + | // consumers are migrated. | |
| 80 | + | type schemeHandlerAdapter struct { | |
| 81 | + | handler *DumbSchemeHandler | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func (a *schemeHandlerAdapter) RegisterScheme(_ string, _ func(uri string) ([]byte, string, error)) { | |
| 85 | + | // DumbSchemeHandler handles the "dumb" scheme exclusively via RegisterPage/RegisterWithContext. | |
| 86 | + | // Generic scheme registration is not yet wired through this adapter. | |
| 87 | + | panic("not implemented — DumbSchemeHandler uses RegisterPage/RegisterWithContext pattern") | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | // --- MessageRouter adapter --- | |
| 91 | + | ||
| 92 | + | // messageRouterAdapter bridges *MessageRouter to port.MessageRouter. | |
| 93 | + | // The internal MessageRouter uses a typed MessageHandler interface rather than | |
| 94 | + | // the simple func(string)(string,error) signature in the port interface. | |
| 95 | + | // PostMessage is also not directly available on the internal type. | |
| 96 | + | type messageRouterAdapter struct { | |
| 97 | + | router *MessageRouter | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | func (a *messageRouterAdapter) RegisterHandler(_ string, _ func(message string) (string, error)) { | |
| 101 | + | // Internal MessageRouter.RegisterHandler takes a MessageHandler interface, not a plain func. | |
| 102 | + | // Wire up via RegisterHandler(msgType, MessageHandlerFunc{...}) when consumers are migrated. | |
| 103 | + | panic("not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly") | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | func (a *messageRouterAdapter) PostMessage(webviewID port.WebViewID, message string) error { | |
| 107 | + | wv := LookupWebView(webviewID) | |
| 108 | + | if wv == nil { | |
| 109 | + | return fmt.Errorf("webview %d not found", webviewID) | |
| 110 | + | } | |
| 111 | + | wv.RunJavaScript(context.Background(), message, "") | |
| 112 | + | return nil | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | // --- SettingsApplier adapter --- | |
| 116 | + | ||
| 117 | + | // settingsApplierAdapter bridges *SettingsManager to port.SettingsApplier. | |
| 118 | + | type settingsApplierAdapter struct { | |
| 119 | + | settings *SettingsManager | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | func (a *settingsApplierAdapter) ApplyToAll(ctx context.Context, webviews []port.WebView) { | |
| 123 | + | for _, wv := range webviews { | |
| 124 | + | if wwv, ok := wv.(*WebView); ok && !wwv.IsDestroyed() { | |
| 125 | + | a.settings.ApplyToWebView(ctx, wwv.Widget()) | |
| 126 | + | } | |
| 127 | + | } | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | // --- FilterApplier adapter --- | |
| 131 | + | ||
| 132 | + | // filterApplierAdapter bridges *filtering.Manager to port.FilterApplier. | |
| 133 | + | type filterApplierAdapter struct { | |
| 134 | + | manager *filtering.Manager | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | func (a *filterApplierAdapter) ApplyToAll(ctx context.Context, webviews []port.WebView) { | |
| 138 | + | for _, wv := range webviews { | |
| 139 | + | if wwv, ok := wv.(*WebView); ok && !wwv.IsDestroyed() { | |
| 140 | + | a.manager.ApplyTo(ctx, wwv.UserContentManager()) | |
| 141 | + | } | |
| 142 | + | } | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | // --- FaviconDatabase adapter --- | |
| 146 | + | ||
| 147 | + | // faviconDatabaseAdapter bridges *WebKitContext to port.FaviconDatabase. | |
| 148 | + | // The WebKitContext exposes the favicon database; async lookup is a stub until | |
| 149 | + | // the coordinator layer is migrated to use this port. | |
| 150 | + | type faviconDatabaseAdapter struct { | |
| 151 | + | wkCtx *WebKitContext | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | func (a *faviconDatabaseAdapter) GetFaviconAsync(_ string, callback func(port.Texture)) { | |
| 155 | + | // FaviconDatabase async lookup is not yet wired through this adapter. | |
| 156 | + | // The underlying API is: wkCtx.FaviconDatabase().GetFavicon(uri, callback). | |
| 157 | + | // This stub satisfies the interface contract; callers will receive nil. | |
| 158 | + | callback(nil) | |
| 159 | + | } | |
| @@ -0,0 +1,159 @@ | |||||
| 1 | + | package webkit | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "fmt" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 8 | + | "github.com/bnema/dumber/internal/infrastructure/filtering" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // --- WebViewFactory adapter --- | |||
| 12 | + | ||||
| 13 | + | // webViewFactoryAdapter bridges *WebViewFactory to port.WebViewFactory. | |||
| 14 | + | // WebViewFactory.Create and CreateRelated return *WebView, not port.WebView, | |||
| 15 | + | // so this adapter wraps the return values. | |||
| 16 | + | type webViewFactoryAdapter struct { | |||
| 17 | + | factory *WebViewFactory | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | func (a *webViewFactoryAdapter) Create(ctx context.Context) (port.WebView, error) { | |||
| 21 | + | wv, err := a.factory.Create(ctx) | |||
| 22 | + | if err != nil { | |||
| 23 | + | return nil, err | |||
| 24 | + | } | |||
| 25 | + | return wv, nil | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | func (a *webViewFactoryAdapter) CreateRelated(ctx context.Context, parentID port.WebViewID) (port.WebView, error) { | |||
| 29 | + | wv, err := a.factory.CreateRelated(ctx, parentID) | |||
| 30 | + | if err != nil { | |||
| 31 | + | return nil, err | |||
| 32 | + | } | |||
| 33 | + | return wv, nil | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | // --- WebViewPool adapter --- | |||
| 37 | + | ||||
| 38 | + | // webViewPoolAdapter bridges *WebViewPool to port.WebViewPool. | |||
| 39 | + | // WebViewPool methods use *WebView and require a context; this adapter adapts | |||
| 40 | + | // the signatures to match the port interface. | |||
| 41 | + | type webViewPoolAdapter struct { | |||
| 42 | + | pool *WebViewPool | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | func (a *webViewPoolAdapter) Acquire(ctx context.Context) (port.WebView, error) { | |||
| 46 | + | wv, err := a.pool.Acquire(ctx) | |||
| 47 | + | if err != nil { | |||
| 48 | + | return nil, err | |||
| 49 | + | } | |||
| 50 | + | return wv, nil | |||
| 51 | + | } | |||
| 52 | + | ||||
| 53 | + | func (a *webViewPoolAdapter) Release(wv port.WebView) { | |||
| 54 | + | if wv == nil { | |||
| 55 | + | return | |||
| 56 | + | } | |||
| 57 | + | if wwv, ok := wv.(*WebView); ok { | |||
| 58 | + | a.pool.Release(context.Background(), wwv) | |||
| 59 | + | } | |||
| 60 | + | } | |||
| 61 | + | ||||
| 62 | + | func (a *webViewPoolAdapter) Prewarm(count int) { | |||
| 63 | + | a.pool.Prewarm(context.Background(), count) | |||
| 64 | + | } | |||
| 65 | + | ||||
| 66 | + | func (a *webViewPoolAdapter) Size() int { | |||
| 67 | + | return a.pool.Size() | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | func (a *webViewPoolAdapter) Close() { | |||
| 71 | + | a.pool.Close(context.Background()) | |||
| 72 | + | } | |||
| 73 | + | ||||
| 74 | + | // --- SchemeHandler adapter --- | |||
| 75 | + | ||||
| 76 | + | // schemeHandlerAdapter bridges *DumbSchemeHandler to port.SchemeHandler. | |||
| 77 | + | // DumbSchemeHandler uses a page-based registration pattern (RegisterPage) rather | |||
| 78 | + | // than a generic scheme+handler registration; this adapter is a stub until | |||
| 79 | + | // consumers are migrated. | |||
| 80 | + | type schemeHandlerAdapter struct { | |||
| 81 | + | handler *DumbSchemeHandler | |||
| 82 | + | } | |||
| 83 | + | ||||
| 84 | + | func (a *schemeHandlerAdapter) RegisterScheme(_ string, _ func(uri string) ([]byte, string, error)) { | |||
| 85 | + | // DumbSchemeHandler handles the "dumb" scheme exclusively via RegisterPage/RegisterWithContext. | |||
| 86 | + | // Generic scheme registration is not yet wired through this adapter. | |||
| 87 | + | panic("not implemented — DumbSchemeHandler uses RegisterPage/RegisterWithContext pattern") | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | // --- MessageRouter adapter --- | |||
| 91 | + | ||||
| 92 | + | // messageRouterAdapter bridges *MessageRouter to port.MessageRouter. | |||
| 93 | + | // The internal MessageRouter uses a typed MessageHandler interface rather than | |||
| 94 | + | // the simple func(string)(string,error) signature in the port interface. | |||
| 95 | + | // PostMessage is also not directly available on the internal type. | |||
| 96 | + | type messageRouterAdapter struct { | |||
| 97 | + | router *MessageRouter | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | func (a *messageRouterAdapter) RegisterHandler(_ string, _ func(message string) (string, error)) { | |||
| 101 | + | // Internal MessageRouter.RegisterHandler takes a MessageHandler interface, not a plain func. | |||
| 102 | + | // Wire up via RegisterHandler(msgType, MessageHandlerFunc{...}) when consumers are migrated. | |||
| 103 | + | panic("not implemented — use MessageRouter.RegisterHandler(msgType, MessageHandler) directly") | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | func (a *messageRouterAdapter) PostMessage(webviewID port.WebViewID, message string) error { | |||
| 107 | + | wv := LookupWebView(webviewID) | |||
| 108 | + | if wv == nil { | |||
| 109 | + | return fmt.Errorf("webview %d not found", webviewID) | |||
| 110 | + | } | |||
| 111 | + | wv.RunJavaScript(context.Background(), message, "") | |||
| 112 | + | return nil | |||
| 113 | + | } | |||
| 114 | + | ||||
| 115 | + | // --- SettingsApplier adapter --- | |||
| 116 | + | ||||
| 117 | + | // settingsApplierAdapter bridges *SettingsManager to port.SettingsApplier. | |||
| 118 | + | type settingsApplierAdapter struct { | |||
| 119 | + | settings *SettingsManager | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | func (a *settingsApplierAdapter) ApplyToAll(ctx context.Context, webviews []port.WebView) { | |||
| 123 | + | for _, wv := range webviews { | |||
| 124 | + | if wwv, ok := wv.(*WebView); ok && !wwv.IsDestroyed() { | |||
| 125 | + | a.settings.ApplyToWebView(ctx, wwv.Widget()) | |||
| 126 | + | } | |||
| 127 | + | } | |||
| 128 | + | } | |||
| 129 | + | ||||
| 130 | + | // --- FilterApplier adapter --- | |||
| 131 | + | ||||
| 132 | + | // filterApplierAdapter bridges *filtering.Manager to port.FilterApplier. | |||
| 133 | + | type filterApplierAdapter struct { | |||
| 134 | + | manager *filtering.Manager | |||
| 135 | + | } | |||
| 136 | + | ||||
| 137 | + | func (a *filterApplierAdapter) ApplyToAll(ctx context.Context, webviews []port.WebView) { | |||
| 138 | + | for _, wv := range webviews { | |||
| 139 | + | if wwv, ok := wv.(*WebView); ok && !wwv.IsDestroyed() { | |||
| 140 | + | a.manager.ApplyTo(ctx, wwv.UserContentManager()) | |||
| 141 | + | } | |||
| 142 | + | } | |||
| 143 | + | } | |||
| 144 | + | ||||
| 145 | + | // --- FaviconDatabase adapter --- | |||
| 146 | + | ||||
| 147 | + | // faviconDatabaseAdapter bridges *WebKitContext to port.FaviconDatabase. | |||
| 148 | + | // The WebKitContext exposes the favicon database; async lookup is a stub until | |||
| 149 | + | // the coordinator layer is migrated to use this port. | |||
| 150 | + | type faviconDatabaseAdapter struct { | |||
| 151 | + | wkCtx *WebKitContext | |||
| 152 | + | } | |||
| 153 | + | ||||
| 154 | + | func (a *faviconDatabaseAdapter) GetFaviconAsync(_ string, callback func(port.Texture)) { | |||
| 155 | + | // FaviconDatabase async lookup is not yet wired through this adapter. | |||
| 156 | + | // The underlying API is: wkCtx.FaviconDatabase().GetFavicon(uri, callback). | |||
| 157 | + | // This stub satisfies the interface contract; callers will receive nil. | |||
| 158 | + | callback(nil) | |||
| 159 | + | } | |||