返回 DeepSeek-Reasonix
chromium.go
根目录 / desktop / third_party / go-webview2 / pkg / edge / chromium.go
1 //go:build windows
2 // +build windows
3
4 package edge
5
6 import (
7 "errors"
8 "fmt"
9 "log"
10 "os"
11 "path/filepath"
12 "runtime"
13 "strings"
14 "sync/atomic"
15 "syscall"
16 "time"
17 "unsafe"
18
19 "github.com/wailsapp/go-webview2/internal/w32"
20 "github.com/wailsapp/go-webview2/webviewloader"
21 "golang.org/x/sys/windows"
22 )
23
24 type Rect = w32.Rect
25
26 // WebView2 must remain the sole owner of its rasterization scale. Disabling
27 // monitor-scale detection leaves frameless windows with stale bounds after a
28 // minimise/restore cycle on mixed-DPI displays (Reasonix #5862, Wails #5544).
29 const (
30 shouldDetectMonitorScaleChanges = true
31 reasonixNoProxyServerBrowserArg = "--no-proxy-server"
32 )
33
34 func globalErrorHandler(err error) {
35 if err == nil {
36 return
37 }
38
39 fmt.Printf("[WebView2 Error] %v\n", err)
40
41 stackBuf := make([]uintptr, 64)
42 stackSize := runtime.Callers(2, stackBuf)
43 frames := runtime.CallersFrames(stackBuf[:stackSize])
44
45 fmt.Println("\nStack trace:")
46 stackIndex := 1
47 for {
48 frame, more := frames.Next()
49 if !more {
50 break
51 }
52 log.Printf("%d: %s\n\t%s:%d\n", stackIndex, frame.Function, frame.File, frame.Line)
53 stackIndex++
54 }
55 }
56
57 type Chromium struct {
58 hwnd uintptr
59 padding struct {
60 Left int32
61 Top int32
62 Right int32
63 Bottom int32
64 }
65
66 controller *ICoreWebView2Controller
67 webview *ICoreWebView2
68 inited uintptr
69 envCompleted *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
70 controllerCompleted *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler
71 webMessageReceived *iCoreWebView2WebMessageReceivedEventHandler
72 containsFullScreenElementChanged *ICoreWebView2ContainsFullScreenElementChangedEventHandler
73 permissionRequested *iCoreWebView2PermissionRequestedEventHandler
74 webResourceRequested *iCoreWebView2WebResourceRequestedEventHandler
75 acceleratorKeyPressed *ICoreWebView2AcceleratorKeyPressedEventHandler
76 navigationCompleted *ICoreWebView2NavigationCompletedEventHandler
77 processFailed *ICoreWebView2ProcessFailedEventHandler
78
79 environment *ICoreWebView2Environment
80 webview2RuntimeVersion string
81
82 // Settings
83 Debug bool
84 DataPath string
85 BrowserPath string
86 AdditionalBrowserArgs []string
87
88 // permissions
89 permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState
90 globalPermission *CoreWebView2PermissionState
91
92 // Callbacks
93 MessageCallback func(message string, sender *ICoreWebView2, args *ICoreWebView2WebMessageReceivedEventArgs)
94 MessageWithAdditionalObjectsCallback func(message string, sender *ICoreWebView2, args *ICoreWebView2WebMessageReceivedEventArgs)
95 WebResourceRequestedCallback func(request *ICoreWebView2WebResourceRequest, args *ICoreWebView2WebResourceRequestedEventArgs)
96 NavigationCompletedCallback func(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs)
97 ProcessFailedCallback func(sender *ICoreWebView2, args *ICoreWebView2ProcessFailedEventArgs)
98 ContainsFullScreenElementChangedCallback func(sender *ICoreWebView2, args *ICoreWebView2ContainsFullScreenElementChangedEventArgs)
99 AcceleratorKeyCallback func(uint) bool
100
101 // Error handling
102 globalErrorCallback func(error)
103
104 shuttingDown bool
105
106 // Resize debouncing
107 lastBounds *w32.Rect
108 resizeTimer *time.Timer
109 }
110
111 func NewChromium() *Chromium {
112 // Reasonix's WebView only loads embedded assets and loopback remote-workspace
113 // pages. Keep that native UI independent from a stale Windows system proxy;
114 // provider, updater, MCP, and SSH traffic use Reasonix's separate Go client.
115 e := &Chromium{AdditionalBrowserArgs: []string{reasonixNoProxyServerBrowserArg}}
116 /*
117 All these handlers are passed to native code through syscalls with 'uintptr(unsafe.Pointer(handler))' and we know
118 that a pointer to those will be kept in the native code. Furthermore these handlers als contain pointer to other Go
119 structs like the vtable.
120 This violates the unsafe.Pointer rule '(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.' because
121 theres no guarantee that Go doesn't move these objects.
122 AFAIK currently the Go runtime doesn't move HEAP objects, so we should be safe with these handlers. But they don't
123 guarantee it, because in the future Go might use a compacting GC.
124 There's a proposal to add a runtime.Pin function, to prevent moving pinned objects, which would allow to easily fix
125 this issue by just pinning the handlers. The https://go-review.googlesource.com/c/go/+/367296/ should land in Go 1.19.
126 */
127 e.envCompleted = newICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler(e)
128 e.controllerCompleted = newICoreWebView2CreateCoreWebView2ControllerCompletedHandler(e)
129 e.webMessageReceived = newICoreWebView2WebMessageReceivedEventHandler(e)
130 e.permissionRequested = newICoreWebView2PermissionRequestedEventHandler(e)
131 e.webResourceRequested = newICoreWebView2WebResourceRequestedEventHandler(e)
132 e.acceleratorKeyPressed = newICoreWebView2AcceleratorKeyPressedEventHandler(e)
133 e.navigationCompleted = newICoreWebView2NavigationCompletedEventHandler(e)
134 e.processFailed = newICoreWebView2ProcessFailedEventHandler(e)
135 e.containsFullScreenElementChanged = newICoreWebView2ContainsFullScreenElementChangedEventHandler(e)
136 /*
137 // Pinner seems to panic in some cases as reported on Discord, maybe during shutdown when GC detects pinned objects
138 // to be released that have not been unpinned.
139 // It would also be better to use our ComBridge for this event handlers implementation instead of pinning them.
140 // So all COM Implementations on the go-side use the same code.
141 var pinner runtime.Pinner
142 pinner.Pin(e.envCompleted)
143 pinner.Pin(e.controllerCompleted)
144 pinner.Pin(e.webMessageReceived)
145 pinner.Pin(e.permissionRequested)
146 pinner.Pin(e.webResourceRequested)
147 pinner.Pin(e.acceleratorKeyPressed)
148 pinner.Pin(e.navigationCompleted)
149 pinner.Pin(e.processFailed)
150 pinner.Pin(e.containsFullScreenElementChanged)
151 */
152 e.permissions = make(map[CoreWebView2PermissionKind]CoreWebView2PermissionState)
153 e.globalErrorCallback = globalErrorHandler
154 return e
155 }
156
157 func (e *Chromium) ShuttingDown() {
158 e.shuttingDown = true
159 }
160
161 func (e *Chromium) errorCallback(err error) {
162 e.globalErrorCallback(err)
163 os.Exit(1)
164 }
165
166 func (e *Chromium) SetErrorCallback(callback func(error)) {
167 if callback != nil {
168 e.globalErrorCallback = callback
169 }
170 }
171
172 func (e *Chromium) Embed(hwnd uintptr) bool {
173
174 var err error
175
176 e.hwnd = hwnd
177
178 dataPath := e.DataPath
179 if dataPath == "" {
180 currentExePath := make([]uint16, windows.MAX_PATH)
181 _, err = windows.GetModuleFileName(windows.Handle(0), &currentExePath[0], windows.MAX_PATH)
182 if err != nil {
183 e.errorCallback(err)
184 }
185 currentExeName := filepath.Base(windows.UTF16ToString(currentExePath))
186 dataPath = filepath.Join(os.Getenv("AppData"), currentExeName)
187 }
188
189 if e.BrowserPath != "" {
190 if _, err = os.Stat(e.BrowserPath); errors.Is(err, os.ErrNotExist) {
191 e.errorCallback(fmt.Errorf("browser path '%s' does not exist", e.BrowserPath))
192 }
193 }
194
195 browserArgs := strings.Join(e.AdditionalBrowserArgs, " ")
196 if err := createCoreWebView2EnvironmentWithOptions(e.BrowserPath, dataPath, e.envCompleted, browserArgs); err != nil {
197 e.errorCallback(fmt.Errorf("error calling Webview2Loader: %s", err.Error()))
198 }
199
200 e.webview2RuntimeVersion, err = webviewloader.GetAvailableCoreWebView2BrowserVersionString(e.BrowserPath)
201 if err != nil {
202 e.errorCallback(fmt.Errorf("error getting Webview2 runtime version: %s", err.Error()))
203 }
204
205 var msg w32.Msg
206 for {
207 if atomic.LoadUintptr(&e.inited) != 0 {
208 break
209 }
210 r, _, _ := w32.User32GetMessageW.Call(
211 uintptr(unsafe.Pointer(&msg)),
212 0,
213 0,
214 0,
215 )
216 if r == 0 {
217 break
218 }
219 w32.User32TranslateMessage.Call(uintptr(unsafe.Pointer(&msg)))
220 w32.User32DispatchMessageW.Call(uintptr(unsafe.Pointer(&msg)))
221 }
222 e.Init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}")
223 return true
224 }
225
226 func (e *Chromium) SetPadding(padding Rect) {
227 if e.padding.Left == padding.Left && e.padding.Top == padding.Top &&
228 e.padding.Right == padding.Right && e.padding.Bottom == padding.Bottom {
229
230 return
231 }
232
233 e.padding.Left = padding.Left
234 e.padding.Top = padding.Top
235 e.padding.Right = padding.Right
236 e.padding.Bottom = padding.Bottom
237 e.Resize()
238 }
239
240 func (e *Chromium) ResizeWithBounds(bounds *Rect) {
241 if e.hwnd == 0 {
242 return
243 }
244
245 bounds.Top += e.padding.Top
246 bounds.Bottom -= e.padding.Bottom
247 bounds.Left += e.padding.Left
248 bounds.Right -= e.padding.Right
249
250 e.SetSize(*bounds)
251 }
252
253 func (e *Chromium) Resize() {
254 if e.hwnd == 0 {
255 return
256 }
257
258 bounds, err := w32.GetClientRect(e.hwnd)
259 if err != nil {
260 e.errorCallback(err)
261 return
262 }
263
264 e.ResizeWithBounds(&bounds)
265 }
266
267 func (e *Chromium) Navigate(url string) {
268 err := e.webview.Navigate(url)
269 if err != nil {
270 e.errorCallback(err)
271 }
272 }
273
274 func (e *Chromium) NavigateToString(content string) {
275 err := e.webview.NavigateToString(content)
276 if err != nil {
277 e.errorCallback(err)
278 }
279 }
280
281 func (e *Chromium) Init(script string) {
282 err := e.webview.AddScriptToExecuteOnDocumentCreated(script, nil)
283 if err != nil {
284 e.errorCallback(err)
285 }
286 }
287
288 func (e *Chromium) Eval(script string) {
289 if e.webview == nil || e.shuttingDown {
290 return
291 }
292
293 err := e.webview.ExecuteScript(script, nil)
294 if err != nil && !errors.Is(err, windows.ERROR_IO_PENDING) {
295 e.errorCallback(err)
296 }
297 }
298
299 func (e *Chromium) Show() error {
300 return e.controller.PutIsVisible(true)
301 }
302
303 func (e *Chromium) Hide() error {
304 return e.controller.PutIsVisible(false)
305 }
306
307 func (e *Chromium) QueryInterface(_, _ uintptr) uintptr {
308 return 0
309 }
310
311 func (e *Chromium) AddRef() uintptr {
312 return 1
313 }
314
315 func (e *Chromium) Release() uintptr {
316 return 1
317 }
318
319 func (e *Chromium) EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr {
320 if env == nil {
321 err := syscall.Errno(res)
322 log.Printf("[WebView2] Environment creation failed with error code %v: %v\n", res, err)
323 if e.globalErrorCallback != nil {
324 e.globalErrorCallback(fmt.Errorf("failed to create WebView2 environment: %w", err))
325 }
326 return res
327 }
328
329 log.Printf("[WebView2] Environment created successfully\n")
330
331 env.vtbl.AddRef.Call(uintptr(unsafe.Pointer(env)))
332 e.environment = env
333
334 err := env.CreateCoreWebView2Controller(e.hwnd, e.controllerCompleted)
335 if err != nil {
336 e.errorCallback(err)
337 }
338 return 0
339 }
340
341 func (e *Chromium) CreateCoreWebView2ControllerCompleted(res uintptr, controller *ICoreWebView2Controller) uintptr {
342 if int32(res) < 0 {
343 e.errorCallback(fmt.Errorf("error creating controller with %08x: %s", res, syscall.Errno(res)))
344 }
345
346 var err error
347
348 controller.vtbl.AddRef.Call(uintptr(unsafe.Pointer(controller)))
349 e.controller = controller
350
351 // Try to get ICoreWebView2Controller3 interface for better performance
352 if controller3 := e.controller.GetICoreWebView2Controller3(); controller3 != nil {
353 // Use raw pixels mode for better performance during resize
354 if err := controller3.PutBoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); err != nil {
355 e.errorCallback(err)
356 }
357
358 // Raw-pixel bounds and automatic monitor-scale detection are orthogonal.
359 // Keep detection enabled so WebView2 updates its scale atomically when a
360 // minimised window is temporarily associated with another monitor.
361 if err := controller3.PutShouldDetectMonitorScaleChanges(shouldDetectMonitorScaleChanges); err != nil {
362 e.errorCallback(err)
363 }
364 }
365 var token _EventRegistrationToken
366 e.webview, err = e.controller.GetCoreWebView2()
367 if err != nil {
368 e.errorCallback(err)
369 }
370
371 e.webview.vtbl.AddRef.Call(uintptr(unsafe.Pointer(e.webview)))
372 err = e.webview.AddWebMessageReceived(e.webMessageReceived, &token)
373 if err != nil {
374 e.errorCallback(err)
375 }
376 err = e.webview.AddPermissionRequested(e.permissionRequested, &token)
377 if err != nil {
378 e.errorCallback(err)
379 }
380 err = e.webview.AddWebResourceRequested(e.webResourceRequested, &token)
381 if err != nil {
382 e.errorCallback(err)
383 }
384 err = e.webview.AddNavigationCompleted(e.navigationCompleted, &token)
385 if err != nil {
386 e.errorCallback(err)
387 }
388 err = e.webview.AddProcessFailed(e.processFailed, &token)
389 if err != nil {
390 e.errorCallback(err)
391 }
392 err = e.webview.AddContainsFullScreenElementChanged(e.containsFullScreenElementChanged, &token)
393 if err != nil {
394 e.errorCallback(err)
395 }
396
397 err = e.controller.AddAcceleratorKeyPressed(e.acceleratorKeyPressed, &token)
398 if err != nil {
399 e.errorCallback(err)
400 }
401
402 atomic.StoreUintptr(&e.inited, 1)
403
404 return 0
405 }
406
407 func (e *Chromium) ContainsFullScreenElementChanged(sender *ICoreWebView2, args *ICoreWebView2ContainsFullScreenElementChangedEventArgs) uintptr {
408 if e.ContainsFullScreenElementChangedCallback != nil {
409 e.ContainsFullScreenElementChangedCallback(sender, args)
410 }
411 return 0
412 }
413
414 func (e *Chromium) MessageReceived(sender *ICoreWebView2, args *ICoreWebView2WebMessageReceivedEventArgs) uintptr {
415 message, err := args.TryGetWebMessageAsString()
416 if err != nil {
417 e.errorCallback(err)
418 }
419
420 if HasCapability(e.webview2RuntimeVersion, GetAdditionalObjects) {
421 obj, err := args.GetAdditionalObjects()
422 if err != nil {
423 e.errorCallback(err)
424 }
425
426 if obj != nil && e.MessageWithAdditionalObjectsCallback != nil {
427 defer obj.Release()
428 e.MessageWithAdditionalObjectsCallback(message, sender, args)
429 } else if e.MessageCallback != nil {
430 e.MessageCallback(message, sender, args)
431 }
432 } else if e.MessageCallback != nil {
433 e.MessageCallback(message, sender, args)
434 }
435
436 err = sender.PostWebMessageAsString(message)
437 if err != nil && !errors.Is(err, windows.ERROR_IO_PENDING) {
438 e.errorCallback(err)
439 }
440 return 0
441 }
442
443 func (e *Chromium) SetPermission(kind CoreWebView2PermissionKind, state CoreWebView2PermissionState) {
444 e.permissions[kind] = state
445 }
446
447 func (e *Chromium) SetBackgroundColour(R, G, B, A uint8) {
448 controller := e.GetController()
449 controller2 := controller.GetICoreWebView2Controller2()
450
451 backgroundCol := COREWEBVIEW2_COLOR{
452 A: A,
453 R: R,
454 G: G,
455 B: B,
456 }
457
458 // WebView2 only has 0 and 255 as valid values.
459 if backgroundCol.A > 0 && backgroundCol.A < 255 {
460 backgroundCol.A = 255
461 }
462
463 err := controller2.PutDefaultBackgroundColor(backgroundCol)
464 if err != nil {
465 e.errorCallback(err)
466 }
467 }
468
469 func (e *Chromium) SetGlobalPermission(state CoreWebView2PermissionState) {
470 e.globalPermission = &state
471 }
472
473 func (e *Chromium) PermissionRequested(_ *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr {
474 kind, err := args.GetPermissionKind()
475 if err != nil {
476 e.errorCallback(err)
477 }
478 var result CoreWebView2PermissionState
479 if e.globalPermission != nil {
480 result = *e.globalPermission
481 } else {
482 var ok bool
483 result, ok = e.permissions[kind]
484 if !ok {
485 result = CoreWebView2PermissionStateDefault
486 }
487 }
488 err = args.PutState(result)
489 if err != nil {
490 e.errorCallback(err)
491 }
492 return 0
493 }
494
495 func (e *Chromium) WebResourceRequested(sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr {
496 req, err := args.GetRequest()
497 if err != nil {
498 log.Fatal(err)
499 }
500 defer req.Release()
501
502 if e.WebResourceRequestedCallback != nil {
503 e.WebResourceRequestedCallback(req, args)
504 }
505 return 0
506 }
507
508 func (e *Chromium) AddWebResourceRequestedFilter(filter string, ctx COREWEBVIEW2_WEB_RESOURCE_CONTEXT) {
509 err := e.webview.AddWebResourceRequestedFilter(filter, ctx)
510 if err != nil {
511 e.errorCallback(err)
512 }
513 }
514
515 func (e *Chromium) Environment() *ICoreWebView2Environment {
516 return e.environment
517 }
518
519 // AcceleratorKeyPressed is called when an accelerator key is pressed.
520 // If the AcceleratorKeyCallback method has been set, it will defer handling of the keypress
521 // to the callback. That callback returns a bool indicating if the event was handled.
522 func (e *Chromium) AcceleratorKeyPressed(sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr {
523 if e.AcceleratorKeyCallback == nil {
524 return 0
525 }
526 eventKind, _ := args.GetKeyEventKind()
527 if eventKind == COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN ||
528 eventKind == COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN {
529 virtualKey, _ := args.GetVirtualKey()
530 status, _ := args.GetPhysicalKeyStatus()
531 if !status.WasKeyDown {
532 err := args.PutHandled(e.AcceleratorKeyCallback(virtualKey))
533 if err != nil {
534 e.errorCallback(err)
535 }
536 } else {
537 return 0
538 }
539 }
540 err := args.PutHandled(false)
541 if err != nil {
542 e.errorCallback(err)
543 }
544 return 0
545 }
546
547 func (e *Chromium) GetSettings() (*ICoreWebViewSettings, error) {
548 return e.webview.GetSettings()
549 }
550
551 func (e *Chromium) GetController() *ICoreWebView2Controller {
552 return e.controller
553 }
554
555 func boolToInt(input bool) int {
556 if input {
557 return 1
558 }
559 return 0
560 }
561
562 func (e *Chromium) NavigationCompleted(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
563 if e.NavigationCompletedCallback != nil {
564 e.NavigationCompletedCallback(sender, args)
565 }
566 return 0
567 }
568
569 func (e *Chromium) ProcessFailed(sender *ICoreWebView2, args *ICoreWebView2ProcessFailedEventArgs) uintptr {
570 if e.ProcessFailedCallback != nil {
571 e.ProcessFailedCallback(sender, args)
572 }
573 return 0
574 }
575
576 func (e *Chromium) NotifyParentWindowPositionChanged() error {
577 //It looks like the wndproc function is called before the controller initialization is complete.
578 //Because of this the controller is nil
579 if e.controller == nil {
580 return nil
581 }
582 return e.controller.NotifyParentWindowPositionChanged()
583 }
584
585 func (e *Chromium) Focus() {
586 err := e.controller.MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC)
587 if err != nil {
588 e.errorCallback(err)
589 }
590 }
591
592 func (e *Chromium) PutZoomFactor(zoomFactor float64) {
593 err := e.controller.PutZoomFactor(zoomFactor)
594 if err != nil {
595 e.errorCallback(err)
596 }
597 }
598
599 func (e *Chromium) OpenDevToolsWindow() {
600 err := e.webview.OpenDevToolsWindow()
601 if err != nil {
602 e.errorCallback(err)
603 }
604 }
605
606 func (e *Chromium) HasCapability(c Capability) bool {
607 return HasCapability(e.webview2RuntimeVersion, c)
608 }
609
610 func (e *Chromium) GetIsSwipeNavigationEnabled() (bool, error) {
611 if !HasCapability(e.webview2RuntimeVersion, SwipeNavigation) {
612 return false, UnsupportedCapabilityError
613 }
614 webview2Settings, err := e.webview.GetSettings()
615 if err != nil {
616 return false, err
617 }
618 webview2Settings6 := webview2Settings.GetICoreWebView2Settings6()
619 var result bool
620 result, err = webview2Settings6.GetIsSwipeNavigationEnabled()
621 if err != nil {
622 return false, err
623 }
624 return result, nil
625 }
626
627 // PutIsGeneralAutofillEnabled controls whether autofill for information
628 // like names, street and email addresses, phone numbers, and arbitrary input
629 // is enabled. This excludes password and credit card information. When
630 // IsGeneralAutofillEnabled is false, no suggestions appear, and no new information
631 // is saved. When IsGeneralAutofillEnabled is true, information is saved, suggestions
632 // appear and clicking on one will populate the form fields.
633 // It will take effect immediately after setting.
634 // The default value is `FALSE`.
635 func (e *Chromium) PutIsGeneralAutofillEnabled(value bool) error {
636 if !HasCapability(e.webview2RuntimeVersion, GeneralAutofillEnabled) {
637 return UnsupportedCapabilityError
638 }
639 webview2Settings, err := e.webview.GetSettings()
640 if err != nil {
641 return err
642 }
643 webview2Settings4 := webview2Settings.GetICoreWebView2Settings4()
644 return webview2Settings4.PutIsGeneralAutofillEnabled(value)
645 }
646
647 // PutIsPasswordAutosaveEnabled sets whether the browser should offer to save passwords and other
648 // identifying information entered into forms automatically.
649 // The default value is `FALSE`.
650 func (e *Chromium) PutIsPasswordAutosaveEnabled(value bool) error {
651 if !HasCapability(e.webview2RuntimeVersion, PasswordAutosaveEnabled) {
652 return UnsupportedCapabilityError
653 }
654 webview2Settings, err := e.webview.GetSettings()
655 if err != nil {
656 return err
657 }
658 webview2Settings4 := webview2Settings.GetICoreWebView2Settings4()
659 return webview2Settings4.PutIsPasswordAutosaveEnabled(value)
660 }
661
662 func (e *Chromium) PutIsSwipeNavigationEnabled(enabled bool) error {
663 if !HasCapability(e.webview2RuntimeVersion, SwipeNavigation) {
664 return UnsupportedCapabilityError
665 }
666 webview2Settings, err := e.webview.GetSettings()
667 if err != nil {
668 return err
669 }
670 webview2Settings6 := webview2Settings.GetICoreWebView2Settings6()
671 err = webview2Settings6.PutIsSwipeNavigationEnabled(enabled)
672 if err != nil {
673 return err
674 }
675 return nil
676 }
677
678 func (e *Chromium) AllowExternalDrag(allow bool) error {
679 if !HasCapability(e.webview2RuntimeVersion, AllowExternalDrop) {
680 return UnsupportedCapabilityError
681 }
682 controller := e.GetController()
683 controller4 := controller.GetICoreWebView2Controller4()
684 err := controller4.PutAllowExternalDrop(allow)
685 if err != nil {
686 return err
687 }
688 return nil
689 }
690
691 func (e *Chromium) GetAllowExternalDrag() (bool, error) {
692 if !HasCapability(e.webview2RuntimeVersion, AllowExternalDrop) {
693 return false, UnsupportedCapabilityError
694 }
695 controller := e.GetController()
696 controller4 := controller.GetICoreWebView2Controller4()
697 result, err := controller4.GetAllowExternalDrop()
698 if err != nil {
699 return false, err
700 }
701 return result, nil
702 }
703
704 func (e *Chromium) GetCookieManager() (*ICoreWebView2CookieManager, error) {
705 if e.webview == nil {
706 return nil, errors.New("webview not initialized")
707 }
708
709 // Check WebView2 version
710 if e.webview2RuntimeVersion == "" {
711 return nil, errors.New("WebView2 runtime version not available")
712 }
713
714 // Get ICoreWebView2_2 interface
715 webview2, err := e.webview.QueryInterface2()
716 if err != nil {
717 return nil, fmt.Errorf("failed to get ICoreWebView2_2: %w\nThis functionality requires WebView2 Runtime version 89.0.721.0 or later. Current version: %s", err, e.webview2RuntimeVersion)
718 }
719 defer webview2.Release()
720
721 // Get cookie manager
722 cookieManager, err := webview2.GetCookieManager()
723 if err != nil {
724 return nil, fmt.Errorf("failed to get cookie manager: %w", err)
725 }
726
727 // Note: The caller is responsible for calling Release() on the returned cookieManager
728 return cookieManager, nil
729 }
730
730 lines GO