refactor(cef): simplify factory using NewClient returning Client
NewClient now returns cef.Client directly (with internal rawPointerHolder), eliminating the clientAdapter workaround. BrowserHostCreateBrowser receives the client cleanly without any raw pointer juggling.
- commit
- 2ccf5f3b898e8f1814d0a19fd6d488c3ffdad182
- author
- brice · · 2026-03-19 19:23 UTC
- committer
- brice · · 2026-03-28 04:36 UTC
- parent
- 144c362
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +10 -68
M internal/infrastructure/cef/factory.go +10 -68
| @@ -54,11 +54,9 @@ func (f *WebViewFactory) Create(_ context.Context) (port.WebView, error) { | |||
| 54 | 54 | input.attachTo(pipeline.glArea) | |
| 55 | 55 | wv.input = input | |
| 56 | 56 | ||
| 57 | - | // Build a CEF client backed by our handlerSet. NewClient allocates a raw | |
| 58 | - | // CEF client struct with ref-counting and callback vtable wired up. We | |
| 59 | - | // keep the pointer alive on the WebView so it is not garbage collected | |
| 60 | - | // before the browser is destroyed. | |
| 61 | - | clientPtr := purecef.NewClient(wv.handlers) | |
| 57 | + | // Build a CEF client backed by our handlerSet. NewClient returns a Client | |
| 58 | + | // that wraps the raw CEF struct with refcounting and callback vtable. | |
| 59 | + | client := purecef.NewClient(wv.handlers) | |
| 62 | 60 | ||
| 63 | 61 | // Configure WindowInfo for off-screen rendering (OSR). | |
| 64 | 62 | var windowInfo purecef.WindowInfo | |
| @@ -70,39 +68,18 @@ func (f *WebViewFactory) Create(_ context.Context) (port.WebView, error) { | |||
| 70 | 68 | settings.Size = unsafe.Sizeof(settings) | |
| 71 | 69 | settings.WindowlessFrameRate = 60 | |
| 72 | 70 | ||
| 73 | - | // Create the browser asynchronously. BrowserHostCreateBrowser posts to the | |
| 74 | - | // CEF UI thread; the browser will be nil until OnAfterCreated fires. | |
| 75 | - | // | |
| 76 | - | // We call the raw creation function directly with the client pointer from | |
| 77 | - | // NewClient, because the high-level wrapper's extractRawPointer requires a | |
| 78 | - | // package-internal interface that external types cannot satisfy. | |
| 79 | - | // | |
| 80 | - | // NOTE: BrowserHostCreateBrowser is currently a stub in purego-cef (returns | |
| 81 | - | // 0). That is expected — the skeleton is correct and will work once the | |
| 82 | - | // stub is implemented. | |
| 83 | - | createBrowser(clientPtr, &windowInfo, &settings) | |
| 84 | - | ||
| 85 | - | return wv, nil | |
| 86 | - | } | |
| 87 | - | ||
| 88 | - | // createBrowser calls purecef.BrowserHostCreateBrowser with the raw client | |
| 89 | - | // pointer. This is separated for testability and to document the impedance | |
| 90 | - | // mismatch between NewClient (returns unsafe.Pointer) and | |
| 91 | - | // BrowserHostCreateBrowser (expects Client interface with unexported | |
| 92 | - | // rawPointer method). | |
| 93 | - | func createBrowser(clientPtr unsafe.Pointer, windowInfo *purecef.WindowInfo, settings *purecef.BrowserSettings) { | |
| 94 | - | // Wrap the raw pointer in a thin Client adapter so BrowserHostCreateBrowser | |
| 95 | - | // can extract it. handlerSet already implements Client; the real vtable is | |
| 96 | - | // in the raw struct that clientPtr points to. | |
| 97 | - | adapter := &clientAdapter{ptr: clientPtr} | |
| 71 | + | // Create the browser asynchronously. The browser will be nil until | |
| 72 | + | // OnAfterCreated fires in the LifeSpanHandler. | |
| 98 | 73 | purecef.BrowserHostCreateBrowser( | |
| 99 | - | windowInfo, | |
| 100 | - | adapter, | |
| 74 | + | &windowInfo, | |
| 75 | + | client, | |
| 101 | 76 | "about:blank", | |
| 102 | - | settings, | |
| 77 | + | &settings, | |
| 103 | 78 | nil, // extraInfo | |
| 104 | 79 | nil, // requestContext | |
| 105 | 80 | ) | |
| 81 | + | ||
| 82 | + | return wv, nil | |
| 106 | 83 | } | |
| 107 | 84 | ||
| 108 | 85 | // CreateRelated creates a WebView that shares session/cookies with the parent. | |
| @@ -111,38 +88,3 @@ func createBrowser(clientPtr unsafe.Pointer, windowInfo *purecef.WindowInfo, set | |||
| 111 | 88 | func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) { | |
| 112 | 89 | return f.Create(ctx) | |
| 113 | 90 | } | |
| 114 | - | ||
| 115 | - | // clientAdapter wraps an unsafe.Pointer from NewClient into something that | |
| 116 | - | // satisfies the purecef.Client interface. The actual CEF callbacks are already | |
| 117 | - | // wired inside the raw struct; these Go methods are never invoked by CEF. | |
| 118 | - | // | |
| 119 | - | // NOTE: purecef.extractRawPointer checks for an unexported rawPointer() | |
| 120 | - | // method, which external packages cannot satisfy. Until purego-cef exports a | |
| 121 | - | // proper WrapClient helper, the pointer extraction will return nil. This is | |
| 122 | - | // acceptable because BrowserHostCreateBrowser is currently a stub. | |
| 123 | - | type clientAdapter struct { | |
| 124 | - | ptr unsafe.Pointer | |
| 125 | - | } | |
| 126 | - | ||
| 127 | - | func (c *clientAdapter) GetAudioHandler() purecef.AudioHandler { return nil } | |
| 128 | - | func (c *clientAdapter) GetCommandHandler() purecef.CommandHandler { return nil } | |
| 129 | - | func (c *clientAdapter) GetContextMenuHandler() purecef.ContextMenuHandler { return nil } | |
| 130 | - | func (c *clientAdapter) GetDialogHandler() purecef.DialogHandler { return nil } | |
| 131 | - | func (c *clientAdapter) GetDisplayHandler() purecef.DisplayHandler { return nil } | |
| 132 | - | func (c *clientAdapter) GetDownloadHandler() purecef.DownloadHandler { return nil } | |
| 133 | - | func (c *clientAdapter) GetDragHandler() purecef.DragHandler { return nil } | |
| 134 | - | func (c *clientAdapter) GetFindHandler() purecef.FindHandler { return nil } | |
| 135 | - | func (c *clientAdapter) GetFocusHandler() purecef.FocusHandler { return nil } | |
| 136 | - | func (c *clientAdapter) GetFrameHandler() purecef.FrameHandler { return nil } | |
| 137 | - | func (c *clientAdapter) GetPermissionHandler() purecef.PermissionHandler { return nil } | |
| 138 | - | func (c *clientAdapter) GetJsdialogHandler() purecef.JsdialogHandler { return nil } | |
| 139 | - | func (c *clientAdapter) GetKeyboardHandler() purecef.KeyboardHandler { return nil } | |
| 140 | - | func (c *clientAdapter) GetLifeSpanHandler() purecef.LifeSpanHandler { return nil } | |
| 141 | - | func (c *clientAdapter) GetLoadHandler() purecef.LoadHandler { return nil } | |
| 142 | - | func (c *clientAdapter) GetPrintHandler() purecef.PrintHandler { return nil } | |
| 143 | - | func (c *clientAdapter) GetRenderHandler() purecef.RenderHandler { return nil } | |
| 144 | - | func (c *clientAdapter) GetRequestHandler() purecef.RequestHandler { return nil } | |
| 145 | - | ||
| 146 | - | func (c *clientAdapter) OnProcessMessageReceived(_ purecef.Browser, _ purecef.Frame, _ purecef.ProcessID, _ purecef.ProcessMessage) int32 { | |
| 147 | - | return 0 | |
| 148 | - | } | |
| @@ -54,11 +54,9 @@ func (f *WebViewFactory) Create(_ context.Context) (port.WebView, error) { | |||||
| 54 | input.attachTo(pipeline.glArea) | 54 | input.attachTo(pipeline.glArea) | ||
| 55 | wv.input = input | 55 | wv.input = input | ||
| 56 | 56 | ||||
| 57 | - | // Build a CEF client backed by our handlerSet. NewClient allocates a raw | 57 | + | // Build a CEF client backed by our handlerSet. NewClient returns a Client |
| 58 | - | // CEF client struct with ref-counting and callback vtable wired up. We | 58 | + | // that wraps the raw CEF struct with refcounting and callback vtable. |
| 59 | - | // keep the pointer alive on the WebView so it is not garbage collected | 59 | + | client := purecef.NewClient(wv.handlers) |
| 60 | - | // before the browser is destroyed. | |||
| 61 | - | clientPtr := purecef.NewClient(wv.handlers) | |||
| 62 | 60 | ||||
| 63 | // Configure WindowInfo for off-screen rendering (OSR). | 61 | // Configure WindowInfo for off-screen rendering (OSR). | ||
| 64 | var windowInfo purecef.WindowInfo | 62 | var windowInfo purecef.WindowInfo | ||
| @@ -70,39 +68,18 @@ func (f *WebViewFactory) Create(_ context.Context) (port.WebView, error) { | |||||
| 70 | settings.Size = unsafe.Sizeof(settings) | 68 | settings.Size = unsafe.Sizeof(settings) | ||
| 71 | settings.WindowlessFrameRate = 60 | 69 | settings.WindowlessFrameRate = 60 | ||
| 72 | 70 | ||||
| 73 | - | // Create the browser asynchronously. BrowserHostCreateBrowser posts to the | 71 | + | // Create the browser asynchronously. The browser will be nil until |
| 74 | - | // CEF UI thread; the browser will be nil until OnAfterCreated fires. | 72 | + | // OnAfterCreated fires in the LifeSpanHandler. |
| 75 | - | // | |||
| 76 | - | // We call the raw creation function directly with the client pointer from | |||
| 77 | - | // NewClient, because the high-level wrapper's extractRawPointer requires a | |||
| 78 | - | // package-internal interface that external types cannot satisfy. | |||
| 79 | - | // | |||
| 80 | - | // NOTE: BrowserHostCreateBrowser is currently a stub in purego-cef (returns | |||
| 81 | - | // 0). That is expected — the skeleton is correct and will work once the | |||
| 82 | - | // stub is implemented. | |||
| 83 | - | createBrowser(clientPtr, &windowInfo, &settings) | |||
| 84 | - | ||||
| 85 | - | return wv, nil | |||
| 86 | - | } | |||
| 87 | - | ||||
| 88 | - | // createBrowser calls purecef.BrowserHostCreateBrowser with the raw client | |||
| 89 | - | // pointer. This is separated for testability and to document the impedance | |||
| 90 | - | // mismatch between NewClient (returns unsafe.Pointer) and | |||
| 91 | - | // BrowserHostCreateBrowser (expects Client interface with unexported | |||
| 92 | - | // rawPointer method). | |||
| 93 | - | func createBrowser(clientPtr unsafe.Pointer, windowInfo *purecef.WindowInfo, settings *purecef.BrowserSettings) { | |||
| 94 | - | // Wrap the raw pointer in a thin Client adapter so BrowserHostCreateBrowser | |||
| 95 | - | // can extract it. handlerSet already implements Client; the real vtable is | |||
| 96 | - | // in the raw struct that clientPtr points to. | |||
| 97 | - | adapter := &clientAdapter{ptr: clientPtr} | |||
| 98 | purecef.BrowserHostCreateBrowser( | 73 | purecef.BrowserHostCreateBrowser( | ||
| 99 | - | windowInfo, | 74 | + | &windowInfo, |
| 100 | - | adapter, | 75 | + | client, |
| 101 | "about:blank", | 76 | "about:blank", | ||
| 102 | - | settings, | 77 | + | &settings, |
| 103 | nil, // extraInfo | 78 | nil, // extraInfo | ||
| 104 | nil, // requestContext | 79 | nil, // requestContext | ||
| 105 | ) | 80 | ) | ||
| 81 | + | ||||
| 82 | + | return wv, nil | |||
| 106 | } | 83 | } | ||
| 107 | 84 | ||||
| 108 | // CreateRelated creates a WebView that shares session/cookies with the parent. | 85 | // CreateRelated creates a WebView that shares session/cookies with the parent. | ||
| @@ -111,38 +88,3 @@ func createBrowser(clientPtr unsafe.Pointer, windowInfo *purecef.WindowInfo, set | |||||
| 111 | func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) { | 88 | func (f *WebViewFactory) CreateRelated(ctx context.Context, _ port.WebViewID) (port.WebView, error) { | ||
| 112 | return f.Create(ctx) | 89 | return f.Create(ctx) | ||
| 113 | } | 90 | } | ||
| 114 | - | ||||
| 115 | - | // clientAdapter wraps an unsafe.Pointer from NewClient into something that | |||
| 116 | - | // satisfies the purecef.Client interface. The actual CEF callbacks are already | |||
| 117 | - | // wired inside the raw struct; these Go methods are never invoked by CEF. | |||
| 118 | - | // | |||
| 119 | - | // NOTE: purecef.extractRawPointer checks for an unexported rawPointer() | |||
| 120 | - | // method, which external packages cannot satisfy. Until purego-cef exports a | |||
| 121 | - | // proper WrapClient helper, the pointer extraction will return nil. This is | |||
| 122 | - | // acceptable because BrowserHostCreateBrowser is currently a stub. | |||
| 123 | - | type clientAdapter struct { | |||
| 124 | - | ptr unsafe.Pointer | |||
| 125 | - | } | |||
| 126 | - | ||||
| 127 | - | func (c *clientAdapter) GetAudioHandler() purecef.AudioHandler { return nil } | |||
| 128 | - | func (c *clientAdapter) GetCommandHandler() purecef.CommandHandler { return nil } | |||
| 129 | - | func (c *clientAdapter) GetContextMenuHandler() purecef.ContextMenuHandler { return nil } | |||
| 130 | - | func (c *clientAdapter) GetDialogHandler() purecef.DialogHandler { return nil } | |||
| 131 | - | func (c *clientAdapter) GetDisplayHandler() purecef.DisplayHandler { return nil } | |||
| 132 | - | func (c *clientAdapter) GetDownloadHandler() purecef.DownloadHandler { return nil } | |||
| 133 | - | func (c *clientAdapter) GetDragHandler() purecef.DragHandler { return nil } | |||
| 134 | - | func (c *clientAdapter) GetFindHandler() purecef.FindHandler { return nil } | |||
| 135 | - | func (c *clientAdapter) GetFocusHandler() purecef.FocusHandler { return nil } | |||
| 136 | - | func (c *clientAdapter) GetFrameHandler() purecef.FrameHandler { return nil } | |||
| 137 | - | func (c *clientAdapter) GetPermissionHandler() purecef.PermissionHandler { return nil } | |||
| 138 | - | func (c *clientAdapter) GetJsdialogHandler() purecef.JsdialogHandler { return nil } | |||
| 139 | - | func (c *clientAdapter) GetKeyboardHandler() purecef.KeyboardHandler { return nil } | |||
| 140 | - | func (c *clientAdapter) GetLifeSpanHandler() purecef.LifeSpanHandler { return nil } | |||
| 141 | - | func (c *clientAdapter) GetLoadHandler() purecef.LoadHandler { return nil } | |||
| 142 | - | func (c *clientAdapter) GetPrintHandler() purecef.PrintHandler { return nil } | |||
| 143 | - | func (c *clientAdapter) GetRenderHandler() purecef.RenderHandler { return nil } | |||
| 144 | - | func (c *clientAdapter) GetRequestHandler() purecef.RequestHandler { return nil } | |||
| 145 | - | ||||
| 146 | - | func (c *clientAdapter) OnProcessMessageReceived(_ purecef.Browser, _ purecef.Frame, _ purecef.ProcessID, _ purecef.ProcessMessage) int32 { | |||
| 147 | - | return 0 | |||
| 148 | - | } | |||