Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# The marshalling tests are the point of this workflow.
#
# pkg/webview2 is generated COM glue, and its failure mode is silent: a wrong argument conversion
# compiles, links, runs and returns S_OK. Eight families of that shipped here, and none would have
# been caught by review or by a build. They are caught by pkg/webview2/marshal_windows_test.go, which
# builds a fake COM object out of Go callbacks and asserts on what actually arrives.
#
# That test needs Windows and nothing else -- no WebView2 Runtime, no Edge, no display, no network --
# so a stock windows-latest runner is enough.
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
# The binding itself, including the marshalling tests.
- run: go test ./...
- run: go vet ./pkg/webview2/

generator:
# The generator is a separate module and pure Go, so it needs no Windows. Its tests include the
# invariant that the committed pkg/webview2 IS the generator's output -- which is what stops a fix
# being applied to the generated files and silently reverted by the next regeneration.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- run: go test ./...
working-directory: scripts
# Cross-compile the generated package for every target it claims to support.
- run: GOOS=windows GOARCH=amd64 go build ./...
- run: GOOS=windows GOARCH=arm64 go build ./pkg/...
7 changes: 0 additions & 7 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ tasks:
- git add .
- git commit -m "Updated mappings"

gofmt:
dir: pkg/webview2
cmds:
- go fmt

test:
cmds:
- go test ./...
Expand All @@ -22,7 +17,6 @@ tasks:
dir: scripts
cmds:
- go run update_version_mapping.go
- task: gofmt
- go fmt update_version_mapping.go
- task: test
- task: commit
Expand All @@ -31,7 +25,6 @@ tasks:
dir: scripts
cmds:
- go run update_version_mapping.go -forced
- task: gofmt
- go fmt update_version_mapping.go
- task: test
- task: commit
8 changes: 4 additions & 4 deletions pkg/webview2/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package webview2
type COREWEBVIEW2_PHYSICAL_KEY_STATUS struct {
RepeatCount uint32
ScanCode uint32
IsExtendedKey bool
IsMenuKeyDown bool
WasKeyDown bool
IsKeyReleased bool
IsExtendedKey int32
IsMenuKeyDown int32
WasKeyDown int32
IsKeyReleased int32
}
45 changes: 29 additions & 16 deletions pkg/webview2/ICoreWebView2.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ func (i *ICoreWebView2) AddRef() uintptr {
return refCounter
}

// Release drops one reference and returns the new count.
//
// AddRef was generated for all 252 interfaces and Release for none, which left every caller of a
// Get<Interface>() accessor leaking: QueryInterface AddRefs on success and there was no matching
// call to make, short of reaching through the embedded IUnknownVtbl for CallRelease. Additive, so
// no existing caller changes.
//
// Not generated for handler interfaces: those are objects WE implement and hand to WebView2, so
// their lifetime is the Go object's, and calling through the vtable would re-enter our own impl.
func (i *ICoreWebView2) Release() uint32 {
return i.Vtbl.CallRelease(unsafe.Pointer(i))
}

func (i *ICoreWebView2) GetSettings() (*ICoreWebView2Settings, error) {

var settings *ICoreWebView2Settings
Expand Down Expand Up @@ -165,7 +178,7 @@ func (i *ICoreWebView2) RemoveNavigationStarting(token EventRegistrationToken) e

hr, _, _ := i.Vtbl.RemoveNavigationStarting.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -192,7 +205,7 @@ func (i *ICoreWebView2) RemoveContentLoading(token EventRegistrationToken) error

hr, _, _ := i.Vtbl.RemoveContentLoading.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -219,7 +232,7 @@ func (i *ICoreWebView2) RemoveSourceChanged(token EventRegistrationToken) error

hr, _, _ := i.Vtbl.RemoveSourceChanged.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -246,7 +259,7 @@ func (i *ICoreWebView2) RemoveHistoryChanged(token EventRegistrationToken) error

hr, _, _ := i.Vtbl.RemoveHistoryChanged.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -273,7 +286,7 @@ func (i *ICoreWebView2) RemoveNavigationCompleted(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemoveNavigationCompleted.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -300,7 +313,7 @@ func (i *ICoreWebView2) RemoveFrameNavigationStarting(token EventRegistrationTok

hr, _, _ := i.Vtbl.RemoveFrameNavigationStarting.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -327,7 +340,7 @@ func (i *ICoreWebView2) RemoveFrameNavigationCompleted(token EventRegistrationTo

hr, _, _ := i.Vtbl.RemoveFrameNavigationCompleted.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -354,7 +367,7 @@ func (i *ICoreWebView2) RemoveScriptDialogOpening(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemoveScriptDialogOpening.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -381,7 +394,7 @@ func (i *ICoreWebView2) RemovePermissionRequested(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemovePermissionRequested.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -408,7 +421,7 @@ func (i *ICoreWebView2) RemoveProcessFailed(token EventRegistrationToken) error

hr, _, _ := i.Vtbl.RemoveProcessFailed.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down Expand Up @@ -552,7 +565,7 @@ func (i *ICoreWebView2) RemoveWebMessageReceived(token EventRegistrationToken) e

hr, _, _ := i.Vtbl.RemoveWebMessageReceived.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down Expand Up @@ -703,7 +716,7 @@ func (i *ICoreWebView2) RemoveNewWindowRequested(token EventRegistrationToken) e

hr, _, _ := i.Vtbl.RemoveNewWindowRequested.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand All @@ -730,7 +743,7 @@ func (i *ICoreWebView2) RemoveDocumentTitleChanged(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemoveDocumentTitleChanged.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down Expand Up @@ -822,7 +835,7 @@ func (i *ICoreWebView2) RemoveContainsFullScreenElementChanged(token EventRegist

hr, _, _ := i.Vtbl.RemoveContainsFullScreenElementChanged.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down Expand Up @@ -865,7 +878,7 @@ func (i *ICoreWebView2) RemoveWebResourceRequested(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemoveWebResourceRequested.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down Expand Up @@ -930,7 +943,7 @@ func (i *ICoreWebView2) RemoveWindowCloseRequested(token EventRegistrationToken)

hr, _, _ := i.Vtbl.RemoveWindowCloseRequested.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&token)),
uintptr(*(*uint64)(unsafe.Pointer(&token))),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down
21 changes: 17 additions & 4 deletions pkg/webview2/ICoreWebView2AcceleratorKeyPressedEventArgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) AddRef() uintptr {
return refCounter
}

// Release drops one reference and returns the new count.
//
// AddRef was generated for all 252 interfaces and Release for none, which left every caller of a
// Get<Interface>() accessor leaking: QueryInterface AddRefs on success and there was no matching
// call to make, short of reaching through the embedded IUnknownVtbl for CallRelease. Additive, so
// no existing caller changes.
//
// Not generated for handler interfaces: those are objects WE implement and hand to WebView2, so
// their lifetime is the Go object's, and calling through the vtable would re-enter our own impl.
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) Release() uint32 {
return i.Vtbl.CallRelease(unsafe.Pointer(i))
}

func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventKind() (COREWEBVIEW2_KEY_EVENT_KIND, error) {

var keyEventKind COREWEBVIEW2_KEY_EVENT_KIND
Expand All @@ -41,9 +54,9 @@ func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventKind() (COREWEB
return keyEventKind, nil
}

func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetVirtualKey() (uint, error) {
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetVirtualKey() (uint32, error) {

var virtualKey uint
var virtualKey uint32

hr, _, _ := i.Vtbl.GetVirtualKey.Call(
uintptr(unsafe.Pointer(i)),
Expand All @@ -55,9 +68,9 @@ func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetVirtualKey() (uint, err
return virtualKey, nil
}

func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventLParam() (int, error) {
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventLParam() (int32, error) {

var lParam int
var lParam int32

hr, _, _ := i.Vtbl.GetKeyEventLParam.Call(
uintptr(unsafe.Pointer(i)),
Expand Down
24 changes: 22 additions & 2 deletions pkg/webview2/ICoreWebView2AcceleratorKeyPressedEventArgs2.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,30 @@ func (i *ICoreWebView2AcceleratorKeyPressedEventArgs2) AddRef() uintptr {
return refCounter
}

func (i *ICoreWebView2) GetICoreWebView2AcceleratorKeyPressedEventArgs2() *ICoreWebView2AcceleratorKeyPressedEventArgs2 {
// Release drops one reference and returns the new count.
//
// AddRef was generated for all 252 interfaces and Release for none, which left every caller of a
// Get<Interface>() accessor leaking: QueryInterface AddRefs on success and there was no matching
// call to make, short of reaching through the embedded IUnknownVtbl for CallRelease. Additive, so
// no existing caller changes.
//
// Not generated for handler interfaces: those are objects WE implement and hand to WebView2, so
// their lifetime is the Go object's, and calling through the vtable would re-enter our own impl.
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs2) Release() uint32 {
return i.Vtbl.CallRelease(unsafe.Pointer(i))
}

func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetICoreWebView2AcceleratorKeyPressedEventArgs2() *ICoreWebView2AcceleratorKeyPressedEventArgs2 {
var result *ICoreWebView2AcceleratorKeyPressedEventArgs2

iidICoreWebView2AcceleratorKeyPressedEventArgs2 := NewGUID("{03b2c8c8-7799-4e34-bd66-ed26aa85f2bf}")
// The HRESULT is deliberately not returned, because changing the signature of all 82 of these
// accessors is an API break. It is E_NOINTERFACE whenever the installed WebView2 Runtime is
// older than this interface, which is the normal case rather than an exotic one -- and then
// result stays nil and the CALLER's next method call dereferences it. Callers must nil-check.
//
// This also leaks a reference on success: QueryInterface AddRefs, and no Release is generated.
// Use Vtbl.CallRelease(unsafe.Pointer(x)) via the embedded IUnknownVtbl when finished.
_, _, _ = i.Vtbl.QueryInterface.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(iidICoreWebView2AcceleratorKeyPressedEventArgs2)),
Expand Down Expand Up @@ -55,7 +75,7 @@ func (i *ICoreWebView2AcceleratorKeyPressedEventArgs2) PutIsBrowserAcceleratorKe

hr, _, _ := i.Vtbl.PutIsBrowserAcceleratorKeyEnabled.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&value)),
boolToUintptr(value),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ func (i *ICoreWebView2BasicAuthenticationRequestedEventArgs) AddRef() uintptr {
return refCounter
}

// Release drops one reference and returns the new count.
//
// AddRef was generated for all 252 interfaces and Release for none, which left every caller of a
// Get<Interface>() accessor leaking: QueryInterface AddRefs on success and there was no matching
// call to make, short of reaching through the embedded IUnknownVtbl for CallRelease. Additive, so
// no existing caller changes.
//
// Not generated for handler interfaces: those are objects WE implement and hand to WebView2, so
// their lifetime is the Go object's, and calling through the vtable would re-enter our own impl.
func (i *ICoreWebView2BasicAuthenticationRequestedEventArgs) Release() uint32 {
return i.Vtbl.CallRelease(unsafe.Pointer(i))
}

func (i *ICoreWebView2BasicAuthenticationRequestedEventArgs) GetUri() (string, error) {
// Create *uint16 to hold result
var _value *uint16
Expand Down Expand Up @@ -95,7 +108,7 @@ func (i *ICoreWebView2BasicAuthenticationRequestedEventArgs) PutCancel(cancel bo

hr, _, _ := i.Vtbl.PutCancel.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(&cancel)),
boolToUintptr(cancel),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
Expand Down
13 changes: 13 additions & 0 deletions pkg/webview2/ICoreWebView2BasicAuthenticationResponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ func (i *ICoreWebView2BasicAuthenticationResponse) AddRef() uintptr {
return refCounter
}

// Release drops one reference and returns the new count.
//
// AddRef was generated for all 252 interfaces and Release for none, which left every caller of a
// Get<Interface>() accessor leaking: QueryInterface AddRefs on success and there was no matching
// call to make, short of reaching through the embedded IUnknownVtbl for CallRelease. Additive, so
// no existing caller changes.
//
// Not generated for handler interfaces: those are objects WE implement and hand to WebView2, so
// their lifetime is the Go object's, and calling through the vtable would re-enter our own impl.
func (i *ICoreWebView2BasicAuthenticationResponse) Release() uint32 {
return i.Vtbl.CallRelease(unsafe.Pointer(i))
}

func (i *ICoreWebView2BasicAuthenticationResponse) GetUserName() (string, error) {
// Create *uint16 to hold result
var _userName *uint16
Expand Down
Loading
Loading