webview.go RawHistoryFolder
1//go:build !webkit_cgo
2
3package webkit
4
5import (
6 "fmt"
7 "log"
8 "strconv"
9 "sync/atomic"
10 "unsafe"
11)
12
13// Package-level ID counter for stub WebViews
14var stubViewIDCounter uint64
15
16// nextStubViewID generates a unique ID for stub WebViews
17func nextStubViewID() uintptr {
18 return uintptr(atomic.AddUint64(&stubViewIDCounter, 1))
19}
20
21// WebView represents a browser view powered by WebKit2GTK.
22// Methods are currently stubs returning ErrNotImplemented to satisfy TDD ordering.
23type WebView struct {
24 config *Config
25 visible bool
26 zoom float64
27 url string
28 destroyed bool
29 window *Window
30 msgHandler func(payload string)
31 titleHandler func(title string)
32 uriHandler func(uri string)
33 faviconHandler func(data []byte)
34 faviconURIHandler func(pageURI, faviconURI string)
35 zoomHandler func(level float64)
36 popupHandler func(string) *WebView
37 closeHandler func()
38 useDomZoom bool
39 domZoomSeed float64
40 container uintptr
41 id uintptr // WebView unique identifier
42
43 // Window type fields (no-op in stub)
44 windowType WindowType
45 windowFeatures *WindowFeatures
46 windowTypeCallback func(WindowType, *WindowFeatures)
47}
48
49// NewWebView constructs a new WebView instance.
50func NewWebView(cfg *Config) (*WebView, error) {
51 if cfg == nil {
52 cfg = &Config{}
53 }
54 log.Printf("[webkit] NewWebView (non-CGO stub) — UI will not be displayed. Build with -tags=webkit_cgo for native window.")
55 wv := &WebView{config: cfg, useDomZoom: cfg.UseDomZoom}
56 if cfg.ZoomDefault <= 0 {
57 wv.zoom = 1.0
58 } else {
59 wv.zoom = cfg.ZoomDefault
60 }
61 wv.domZoomSeed = wv.zoom
62 wv.id = nextStubViewID() // Assign unique ID
63 // Construction succeeds; create a logical window placeholder.
64 wv.window = &Window{Title: "Dumber Browser"}
65 wv.container = newWidgetHandle()
66 return wv, nil
67}
68
69// NewWebViewWithRelated creates a new WebView related to an existing one (non-CGO stub)
70func NewWebViewWithRelated(cfg *Config, relatedView *WebView) (*WebView, error) {
71 // In non-CGO build, just create a regular WebView
72 return NewWebView(cfg)
73}
74
75// LoadURL navigates the webview to the specified URL.
76func (w *WebView) LoadURL(rawURL string) error {
77 if w == nil || w.destroyed {
78 return ErrNotImplemented
79 }
80 if rawURL == "" {
81 return fmt.Errorf("url cannot be empty")
82 }
83 // Minimal validation; actual navigation handled by CGO bridge later.
84 w.url = rawURL
85 log.Printf("[webkit] LoadURL (non-CGO stub): %s", rawURL)
86 return nil
87}
88
89// Show makes the WebView visible.
90func (w *WebView) Show() error {
91 if w == nil || w.destroyed {
92 return ErrNotImplemented
93 }
94 w.visible = true
95 log.Printf("[webkit] Show (non-CGO stub): no native window will appear")
96 return nil
97}
98
99// Hide hides the WebView window.
100func (w *WebView) Hide() error {
101 if w == nil || w.destroyed {
102 return ErrNotImplemented
103 }
104 w.visible = false
105 return nil
106}
107
108// Destroy releases native resources.
109func (w *WebView) Destroy() error {
110 if w == nil {
111 return ErrNotImplemented
112 }
113 w.destroyed = true
114 return nil
115}
116
117// IsDestroyed returns whether this WebView has been destroyed
118func (w *WebView) IsDestroyed() bool {
119 return w != nil && w.destroyed
120}
121
122// Window returns the associated native window wrapper (non-nil).
123func (w *WebView) Window() *Window { return w.window }
124
125// GetCurrentURL returns the last requested URL (non-CGO build approximation).
126func (w *WebView) GetCurrentURL() string { return w.url }
127
128// GoBack is not supported in the non-CGO stub.
129func (w *WebView) GoBack() error { return ErrNotImplemented }
130
131// GoForward is not supported in the non-CGO stub.
132func (w *WebView) GoForward() error { return ErrNotImplemented }
133
134// Reload is not supported in the non-CGO stub.
135func (w *WebView) Reload() error { return ErrNotImplemented }
136
137// ReloadBypassCache is not supported in the non-CGO stub.
138func (w *WebView) ReloadBypassCache() error { return ErrNotImplemented }
139
140// ShowDevTools is a no-op in the non-CGO build.
141func (w *WebView) ShowDevTools() error { return nil }
142
143// CloseDevTools is a no-op in the non-CGO build.
144func (w *WebView) CloseDevTools() error { return nil }
145
146// ShowPrintDialog is a no-op in the non-CGO build.
147func (w *WebView) ShowPrintDialog() error { return nil }
148
149// RegisterScriptMessageHandler registers a callback invoked when the content script posts a message.
150func (w *WebView) RegisterScriptMessageHandler(cb func(payload string)) { w.msgHandler = cb }
151
152func (w *WebView) RegisterPopupHandler(cb func(string) *WebView) { w.popupHandler = cb }
153
154func (w *WebView) RegisterCloseHandler(cb func()) { w.closeHandler = cb }
155
156func (w *WebView) dispatchScriptMessage(payload string) { //nolint:unused // Called from CGO WebKit callbacks
157 if w != nil && w.msgHandler != nil {
158 w.msgHandler(payload)
159 }
160}
161
162func (w *WebView) dispatchPopupRequest(uri string) *WebView { //nolint:unused // Called from CGO WebKit callbacks
163 if w != nil && w.popupHandler != nil {
164 return w.popupHandler(uri)
165 }
166 return nil
167}
168
169// GetNativePointer returns nil for non-CGO build (stub implementation)
170func (w *WebView) GetNativePointer() unsafe.Pointer { //nolint:unused // Needed for interface compatibility
171 return nil
172}
173
174// RegisterTitleChangedHandler registers a callback invoked when the page title changes.
175func (w *WebView) RegisterTitleChangedHandler(cb func(title string)) { w.titleHandler = cb }
176
177func (w *WebView) dispatchTitleChanged(title string) { //nolint:unused // Called from CGO WebKit callbacks
178 if w != nil && w.titleHandler != nil {
179 w.titleHandler(title)
180 }
181}
182
183// RegisterURIChangedHandler registers a callback invoked when the current page URI changes.
184func (w *WebView) RegisterURIChangedHandler(cb func(uri string)) { w.uriHandler = cb }
185
186func (w *WebView) dispatchURIChanged(uri string) { //nolint:unused // Called from CGO WebKit callbacks
187 if w != nil && w.uriHandler != nil {
188 w.uriHandler(uri)
189 }
190}
191
192// RegisterFaviconChangedHandler registers a callback invoked when the page favicon changes.
193func (w *WebView) RegisterFaviconChangedHandler(cb func(data []byte)) { w.faviconHandler = cb }
194
195// RegisterFaviconURIChangedHandler registers a callback invoked when WebKit detects a favicon URI.
196func (w *WebView) RegisterFaviconURIChangedHandler(cb func(pageURI, faviconURI string)) {
197 w.faviconURIHandler = cb
198}
199
200func (w *WebView) dispatchFaviconChanged(data []byte) { //nolint:unused // Called from CGO WebKit callbacks
201 if w != nil && w.faviconHandler != nil {
202 w.faviconHandler(data)
203 }
204}
205
206// RegisterZoomChangedHandler registers a callback invoked when zoom level changes.
207func (w *WebView) RegisterZoomChangedHandler(cb func(level float64)) { w.zoomHandler = cb }
208
209func (w *WebView) dispatchZoomChanged(level float64) {
210 if w != nil && w.zoomHandler != nil {
211 w.zoomHandler(level)
212 }
213}
214
215// Widget returns an empty handle in stub builds.
216func (w *WebView) Widget() uintptr { return 0 }
217
218// RootWidget returns the container widget handle in CGO builds. Stub returns 0.
219func (w *WebView) RootWidget() uintptr { return w.container }
220
221// DestroyWindow is a no-op in stub builds.
222func (w *WebView) DestroyWindow() {}
223
224// RunOnMainThread executes fn immediately in non-CGO builds.
225func (w *WebView) RunOnMainThread(fn func()) {
226 if fn != nil {
227 fn()
228 }
229}
230
231// PrepareForReparenting is a no-op in stub builds.
232func (w *WebView) PrepareForReparenting() {}
233
234// RefreshAfterReparenting is a no-op in stub builds.
235func (w *WebView) RefreshAfterReparenting() {}
236
237// UsesDomZoom reports whether DOM-based zoom is enabled in this WebView.
238func (w *WebView) UsesDomZoom() bool {
239 return w != nil && w.useDomZoom
240}
241
242// SeedDomZoom stores the desired DOM zoom level for the next navigation (stub implementation).
243func (w *WebView) SeedDomZoom(level float64) {
244 if w == nil || !w.useDomZoom {
245 return
246 }
247 if level < 0.25 {
248 level = 0.25
249 } else if level > 5.0 {
250 level = 5.0
251 }
252 w.domZoomSeed = level
253}
254
255// CreateRelatedView returns a new WebView (stub creates independent)
256func (w *WebView) CreateRelatedView() *WebView {
257 nw, _ := NewWebView(w.config)
258 return nw
259}
260
261// OnWindowTypeDetected registers a callback (stored but never invoked in stub)
262func (w *WebView) OnWindowTypeDetected(callback func(WindowType, *WindowFeatures)) {
263 if w != nil {
264 w.windowTypeCallback = callback
265 }
266}
267
268// InitializeContentBlocking initializes WebKit content blocking with filter manager (stub)
269func (w *WebView) InitializeContentBlocking(filterManager interface{}) error {
270 return ErrNotImplemented
271}
272
273// OnNavigate sets up domain-specific cosmetic filtering on navigation (stub)
274func (w *WebView) OnNavigate(url string, filterManager interface{}) {
275 // No-op in stub
276}
277
278// UpdateContentFilters updates the content filters dynamically (stub)
279func (w *WebView) UpdateContentFilters(filterManager interface{}) error {
280 return ErrNotImplemented
281}
282
283// SetWindowFeatures sets the window features for this WebView (stub)
284func (w *WebView) SetWindowFeatures(features *WindowFeatures) {
285 w.windowFeatures = features
286}
287
288// SetActive sets whether this WebView is currently active/focused (stub)
289func (w *WebView) SetActive(active bool) {
290 // No-op in stub
291}
292
293// IsActive returns whether this WebView is currently active/focused (stub)
294func (w *WebView) IsActive() bool {
295 return false
296}
297
298// ID returns the unique identifier for this WebView as a string (stub)
299func (w *WebView) ID() string {
300 if w == nil {
301 return ""
302 }
303 return strconv.FormatUint(uint64(w.id), 10)
304}
305
306// PrefersDarkTheme returns true if GTK is configured to prefer dark theme (stub)
307func PrefersDarkTheme() bool {
308 return false // Fallback to light theme when not using CGO
309}