├── LICENSE.md ├── README.md ├── giowebview ├── giowebview.go ├── giowebview_android.go ├── giowebview_ios.go ├── giowebview_js.go ├── giowebview_macos.go ├── giowebview_unsupported.go ├── giowebview_windows.go ├── lists.go └── pool.go ├── go.mod ├── go.sum ├── installview ├── install.go ├── install_windows.go └── install_windows_test.go └── webview ├── config.go ├── config_android.go ├── config_ios.go ├── config_js.go ├── config_macos.go ├── config_unsupported.go ├── config_windows.go ├── error.go ├── events.go ├── internal ├── fan.go ├── handle.go └── scheduler.go ├── javascript.go ├── javascript_android.go ├── javascript_darwin.go ├── javascript_js.go ├── javascript_windows.go ├── options.go ├── options_android.go ├── options_windows.go ├── storage.go ├── storage_all.go ├── storage_android.go ├── storage_darwin.go ├── storage_js.go ├── storage_windows.go ├── sys_android.go ├── sys_android.jar ├── sys_android.java ├── sys_darwin.go ├── sys_darwin.m ├── sys_js.go ├── sys_js.js ├── sys_js_wasm.go ├── sys_js_wasm.s ├── sys_windows.go ├── sys_windows_386.dll ├── sys_windows_386.go ├── sys_windows_amd64.dll ├── sys_windows_amd64.go ├── sys_windows_arm64.dll ├── sys_windows_arm64.go ├── webview.go ├── webview_all.go ├── webview_android.go ├── webview_darwin.go ├── webview_js.go ├── webview_unsupported.go └── webview_windows.go /LICENSE.md: -------------------------------------------------------------------------------- 1 | All files except the pre-compiled DLLs are licensed under the MIT license: 2 | 3 | MIT License 4 | 5 | Copyright (c) 2022 inkeliz 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | ------------------- 26 | 27 | All pre-compiled DLLs are licensed under the BSD license: 28 | 29 | Copyright (C) Microsoft Corporation. All rights reserved. 30 | 31 | Redistribution and use in source and binary forms, with or without 32 | modification, are permitted provided that the following conditions are 33 | met: 34 | 35 | * Redistributions of source code must retain the above copyright 36 | notice, this list of conditions and the following disclaimer. 37 | * Redistributions in binary form must reproduce the above 38 | copyright notice, this list of conditions and the following disclaimer 39 | in the documentation and/or other materials provided with the 40 | distribution. 41 | * The name of Microsoft Corporation, or the names of its contributors 42 | may not be used to endorse or promote products derived from this 43 | software without specific prior written permission. 44 | 45 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 46 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 47 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 48 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 49 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 51 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 52 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 53 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 54 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 55 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GioWebview 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/inkeliz/giowebview.svg)](https://pkg.go.dev/github.com/inkeliz/giowebview) 4 | 5 | Give some Webview to your Gio and Golang application. 😎 6 | 7 | This project was designed to work with Gio, but it's also possible to use without Gio, providing any HWND (Windows), NSView (macOS), UIView (iOS), HTMLElement (WebAssembly), ViewGroup (Android). Because of that, exists two packages: "GioWebview" and "Webview". 8 | 9 | **Currently, GioWebview doesn't work with Gio out-of-box and requires some patches to work.** 10 | 11 | > This project is still experimental, so please report any issues you find. 😊 12 | 13 | ------------------ 14 | 15 | ## How to use it with Gio? 16 | 17 | 1. **Hook into the Event Loop**: That is necessary to send operations and receive events. 18 | 19 | ```diff 20 | for evt := range w.Events() { // Gio main event loop 21 | + giowebview.Plugin(w, evt) 22 | 23 | switch evt := evt.(type) { 24 | // ... 25 | } 26 | } 27 | ``` 28 | 29 | 2. **Initialize your Webview:** Similar to `widget.Clickable` (and others stateful widgets) you need to create the handler. The handler can be created using `giowebview.NewWebViewOp()`, the handler must be re-used between frames. 30 | 31 | ```go 32 | if myWebview == nil { 33 | myWebview = giowebview.NewWebViewOp() 34 | 35 | stack := myWebview.Push(gtx.Ops) 36 | giowebview.NavigateOp{URL: "https://gioui.org"}.Add(gtx.Ops) 37 | stack.Pop(gtx.Ops) 38 | } 39 | ``` 40 | 3. **Display your WebView:** Similar to `paint.PaintOp` or `clip.RRect` you can set the area which the WebView will show. *The position is absolute and doesn't consider other clips, offsets or transformations*. You can use `giowebview.RectOp{Size: size}.Add(gtx.Ops)` to display the content, and use `giowebview.OffsetOp` to offset. 41 | 42 | ```go 43 | defer w.WebView.Push(gtx.Ops).Pop(gtx.Ops) 44 | 45 | giowebview.OffsetOp{Point: f32.Point{X: 100, Y: 100}}.Add(gtx.Ops) 46 | giowebview.RectOp{Size: f32.Point{X: 500, Y: 500}}.Add(gtx.Ops) 47 | ``` 48 | 49 | There are more features such as read/write cookies, session storage and local storage. Execute javascript and get callbacks from javascript to Golang. 50 | 51 | -------------- 52 | 53 | ## Features 54 | 55 | We are capable of more than just displaying one webpage. 56 | 57 | | OS | Windows | Android | MacOS | iOS | WebAssembly | 58 | | -- | -- | -- | -- | -- | -- | 59 | | Basic Support |✔|✔|✔|✔|✔| 60 | | Setup: Custom Proxy |✔***|✔***|❌|❌|❌| 61 | | Setup: Custom Certificate |✔***|✔***|❌|❌|❌| 62 | | Cookies: Read |✔|✔|✔*|✔|❌| 63 | | Cookies: Write |✔|✔|✔*|✔|❌| 64 | | Cookies: Delete |✔|✔|❌*|✔|❌| 65 | | LocalStorage: Read |✔|✔|✔|✔|❌| 66 | | LocalStorage: Write |✔|✔|✔|✔|❌| 67 | | LocalStorage: Delete |✔|✔|✔|✔|❌| 68 | | SessionStorage: Write |✔|✔|✔|✔|❌| 69 | | SessionStorage: Read |✔|✔|✔|✔|❌| 70 | | SessionStorage: Delete |✔|✔|✔|✔|❌| 71 | | Javascript: Execute |✔|✔|✔|✔|❌| 72 | | Javascript: Install |✔|✔|✔|✔|❌| 73 | | Javascript: Callback |✔**|✔**|✔**|✔**|❌| 74 | | Events: NavigationChange |✔|✔|✔|✔|❌| 75 | | Events: TitleChange |✔|✔|✔|✔|❌| 76 | 77 | - ❌ = Not supported. 78 | - ✔ = Supported. 79 | 80 | - \* = Cookies can be shared across multiple instances of the WebView. Information from the cookie can be incomplete and lack metadata. 81 | - ** = Only accepts a string as argument (other types are not supported and might be encoded as text). 82 | - *** = Must be defined before the WebView is created and is shared with all instances. 83 | 84 | # APIs 85 | 86 | Each operating system has uniqueAPI. For Windows 10+, we use WebView2. For Android 6+, we use WebView. For MacOS and 87 | iOS, we use WKWebView. For WebAssembly, the HTMLIFrameElement is used. 88 | 89 | # Requirements 90 | 91 | - Windows: 92 | - End-Users: must have Windows 7+ and WebView2 installed (you can install it on the user's machine using the `installview` package). 93 | - Developers: must have Golang 1.18+ installed (no CGO required). 94 | - WebAssembly: 95 | - End-Users: must have WebAssembly enabled browser (usually Safari 13+, Chrome 70+). 96 | - Developers: must have Golang 1.18+ installed (no CGO required). 97 | - Contributors: must have InkWasm installed. 98 | - macOS: 99 | - End-Users: must have macOS 11+. 100 | - Developers: must have macOS device with Golang, Xcode, and CLang installed. 101 | - iOS: 102 | - End-Users: must have macOS 11+. 103 | - Developers: must have macOS device with Golang, Xcode, and CLang installed. 104 | - Android: 105 | - End-Users: must have Android 6 or later. 106 | - Developers: must have Golang 1.18+, OpenJDK 1.8, Android NDK, Android SDK 31+ installed ([here for more information](https://gioui.org/doc/install/android)). 107 | - Contributors: must have Android SDK 30 installed. 108 | 109 | # Limitations 110 | 111 | 1. Currently, GioWebview is always the top-most view/window and can't be overlapped by any other draw operation in Gio. 112 | 2. Render multiple webviews at the same time might cause unexpected behaviour, related to z-indexes. 113 | 3. On Javascript/WebAssembly, it needs to be allowed to iframe the content, which most websites blocks such operation. 114 | 4. It's not possible to use WebView using custom shapes (e.g. rounded corners) or apply transformations (e.g. rotating). 115 | 116 | # Security 117 | 118 | This project uses unsafe CGO for some OSes and shares pointers between Go, Javascript, and other languages, which may expose the application. Some mitigations are possible but not fully implemented, and functional exploits are unknown. Furthermore, GioWebview uses the WebView installed in the system, which can be untrusted or out-of-date. 119 | 120 | # License 121 | 122 | The source code is licensed under the MIT license. 123 | The pre-compiled DLLs (such as `/webview/sys_windows_386.dll`, `/webview/sys_windows_amd64.dll` and `/webview/sys_windows_arm64.dll`) is redistributed under the BSD license. See LICENSE.md for more information. 124 | -------------------------------------------------------------------------------- /giowebview/giowebview.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "image" 5 | "net/url" 6 | "runtime" 7 | "sync" 8 | "unsafe" 9 | 10 | "gioui.org/unit" 11 | "github.com/inkeliz/giowebview/webview" 12 | 13 | "gioui.org/app" 14 | "gioui.org/f32" 15 | "gioui.org/io/event" 16 | "gioui.org/io/system" 17 | "gioui.org/op" 18 | ) 19 | 20 | type plugin struct { 21 | queue *queue 22 | 23 | seemMutex sync.Mutex 24 | seem map[webview.WebView]bool 25 | bounds map[webview.WebView]*[2]f32.Point 26 | 27 | active webview.WebView 28 | 29 | config webview.Config 30 | viewEvent app.ViewEvent 31 | } 32 | 33 | type queue struct { 34 | event.Queue 35 | events map[event.Tag][]event.Event 36 | mutex sync.Mutex 37 | } 38 | 39 | // Events returns events for the given event.Tag. 40 | // It will return Gio events and WebView events. 41 | func (q *queue) Events(t event.Tag) []event.Event { 42 | q.mutex.Lock() 43 | defer q.mutex.Unlock() 44 | 45 | if w, ok := t.(*WebViewOp); ok { 46 | if w != nil { 47 | t = w.tag 48 | } 49 | } 50 | if evts, ok := q.events[t]; ok || len(evts) > 0 { 51 | q.events[t] = append(q.events[t], q.Queue.Events(t)...) 52 | r := evts 53 | q.events[t] = evts[:0] 54 | return r 55 | } 56 | return q.Queue.Events(t) 57 | } 58 | 59 | func (q *queue) add(t event.Tag, evt event.Event) { 60 | q.mutex.Lock() 61 | defer q.mutex.Unlock() 62 | 63 | q.events[t] = append(q.events[t], evt) 64 | } 65 | 66 | // gioInternalOps must match with gioui.org/op/ops 67 | // That is internal, we use unsafe. >:) 68 | type gioInternalOps struct { 69 | version int 70 | data []byte 71 | refs []interface{} 72 | } 73 | 74 | // Plugin hijacks Gio events and wraps them in WebView events. 75 | // You must call at the beginning of your window.Event function, like this: 76 | // 77 | // for evt := range w.Events() { 78 | // giowebview.Plugin(w, evt) 79 | // 80 | // switch evt := evt.(type) { 81 | // // ... 82 | // } 83 | // } 84 | func Plugin(w *app.Window, evt event.Event) { 85 | p, ok := getPlugin(w) 86 | if !ok { 87 | p = &plugin{ 88 | queue: &queue{events: make(map[event.Tag][]event.Event)}, 89 | seem: make(map[webview.WebView]bool, 8), 90 | bounds: make(map[webview.WebView]*[2]f32.Point, 8), 91 | } 92 | setPlugin(w, p) 93 | } 94 | 95 | switch e := evt.(type) { 96 | case app.ViewEvent: 97 | p.viewEvent = e 98 | config := NewConfigFromViewEvent(w, e) 99 | for v := range p.seem { 100 | if v == nil { 101 | continue 102 | } 103 | v.Configure(config) 104 | } 105 | p.config = config 106 | case system.DestroyEvent: 107 | p.seemMutex.Lock() 108 | for s := range p.seem { 109 | s.Close() 110 | delete(p.seem, s) 111 | } 112 | p.seemMutex.Unlock() 113 | case system.FrameEvent: 114 | ref := *(**system.FrameEvent)(unsafe.Add(unsafe.Pointer(&evt), unsafe.Sizeof(uintptr(0)))) 115 | 116 | p.queue.Queue = ref.Queue 117 | ref.Queue = p.queue 118 | 119 | fn := ref.Frame 120 | ref.Frame = func(frame *op.Ops) { 121 | ops := (*gioInternalOps)(unsafe.Pointer(&frame.Internal)) 122 | fn(frame) 123 | for s := range p.seem { 124 | p.seem[s] = false 125 | } 126 | 127 | for i := range ops.refs { 128 | switch v := ops.refs[i].(type) { 129 | case *RectOp: 130 | p.seem[p.active] = true 131 | v.execute(w, p, e) 132 | case interface { 133 | execute(w *app.Window, p *plugin, _ system.FrameEvent) 134 | }: 135 | v.execute(w, p, e) 136 | default: 137 | _ = v 138 | } 139 | } 140 | 141 | for k, v := range p.seem { 142 | if k != nil && !v { 143 | k.Resize(webview.Point{}, webview.Point{}) 144 | } 145 | } 146 | 147 | for _, v := range p.bounds { 148 | v[0] = f32.Point{} 149 | v[1] = f32.Point{} 150 | } 151 | 152 | p.active = nil 153 | 154 | } 155 | } 156 | } 157 | 158 | // WebViewOp shows the webview into the specified area. 159 | // The RectOp is not context-aware, and will overlay 160 | // any other widget on the screen. 161 | // 162 | // WebViewOp also takes the foreground and clicks events 163 | // and keyboard events will not be routed to Gio. 164 | // 165 | // Performance: changing the size/bounds or radius can 166 | // be expensive. If applicable, change the Offset, instead 167 | // of changing the size. 168 | type WebViewOp struct { 169 | wvTag *int64 170 | tag event.Tag 171 | isPop bool 172 | } 173 | 174 | type webViewOp struct { 175 | wvTag uintptr // *int64 (avoid GC track) 176 | tag event.Tag 177 | isPop bool 178 | } 179 | 180 | // NewWebViewOp creates a new WebViewOp. 181 | func NewWebViewOp() *WebViewOp { 182 | r := &WebViewOp{wvTag: new(int64)} 183 | runtime.SetFinalizer(r, func(r *WebViewOp) { 184 | if v, ok := getWebView(uintptr(unsafe.Pointer(r.wvTag))); ok { 185 | v.Close() 186 | _PluginList.Range(func(_, p interface{}) bool { 187 | p.(*plugin).seemMutex.Lock() 188 | defer p.(*plugin).seemMutex.Unlock() 189 | 190 | delete(p.(*plugin).seem, v) 191 | return true 192 | }) 193 | removeWebView(r.wvTag) 194 | } 195 | }) 196 | 197 | r.tag = event.Tag(uintptr(unsafe.Pointer(r))) 198 | return r 199 | } 200 | 201 | var poolWebViewOp = newPool[webViewOp](func() any { return new(webViewOp) }) 202 | 203 | // Push adds a new WebViewOp to the queue, any subsequent Ops (sucha as RectOp) 204 | // will affect this WebViewOp. 205 | // In order to stop using this WebViewOp, call Pop. 206 | func (o WebViewOp) Push(op *op.Ops) WebViewOp { 207 | o.isPop = false 208 | poolWebViewOp.add(op, *(*webViewOp)(unsafe.Pointer(&o))) 209 | return o 210 | } 211 | 212 | // Pop stops using the WebViewOp. 213 | func (o WebViewOp) Pop(op *op.Ops) { 214 | o.isPop = true 215 | poolWebViewOp.add(op, *(*webViewOp)(unsafe.Pointer(&o))) 216 | } 217 | 218 | func (o *webViewOp) execute(w *app.Window, p *plugin, e system.FrameEvent) { 219 | defer poolWebViewOp.free(o) 220 | 221 | if o.isPop { 222 | p.active = nil 223 | return 224 | } 225 | p.seemMutex.Lock() 226 | defer p.seemMutex.Unlock() 227 | 228 | runner, ok := getWebView(o.wvTag) 229 | if !ok { 230 | wv, err := webview.NewWebView(NewConfigFromViewEvent(w, p.viewEvent)) 231 | if err != nil { 232 | panic(err) 233 | } 234 | go eventsListener(wv, w, p, o.tag) 235 | runner = wv 236 | setWebView(o.wvTag, runner) 237 | } 238 | 239 | if _, ok := p.seem[runner]; !ok { 240 | p.seem[runner] = false 241 | } 242 | 243 | p.active = runner 244 | } 245 | 246 | func eventsListener(wv webview.WebView, w *app.Window, p *plugin, tag event.Tag) { 247 | for evt := range wv.Events() { 248 | switch evt := evt.(type) { 249 | case webview.NavigationEvent: 250 | p.queue.add(tag, NavigationEvent(evt)) 251 | case webview.TitleEvent: 252 | p.queue.add(tag, TitleEvent(evt)) 253 | } 254 | w.Invalidate() 255 | } 256 | } 257 | 258 | // OffsetOp moves the webview by the specified offset. 259 | type OffsetOp struct { 260 | Point f32.Point 261 | } 262 | 263 | // Offset creates a new OffsetOp. 264 | func Offset[POINT image.Point | f32.Point](v POINT) OffsetOp { 265 | switch v := any(v).(type) { 266 | case image.Point: 267 | return OffsetOp{Point: f32.Point{X: float32(v.X), Y: float32(v.Y)}} 268 | case f32.Point: 269 | return OffsetOp{Point: v} 270 | default: 271 | return OffsetOp{} 272 | } 273 | } 274 | 275 | var poolOffsetOp = newPool[OffsetOp](func() any { return new(OffsetOp) }) 276 | 277 | // Add adds a new OffsetOp to the queue. 278 | func (o OffsetOp) Add(op *op.Ops) { 279 | poolOffsetOp.add(op, o) 280 | } 281 | 282 | func (o *OffsetOp) execute(w *app.Window, p *plugin, e system.FrameEvent) { 283 | defer poolOffsetOp.free(o) 284 | if _, ok := p.bounds[p.active]; !ok { 285 | p.bounds[p.active] = new([2]f32.Point) 286 | } 287 | p.bounds[p.active][0].Y += o.Point.Y 288 | p.bounds[p.active][0].X += o.Point.X 289 | } 290 | 291 | // RectOp shows the webview into the specified area. 292 | // The RectOp is not context-aware, and will overlay 293 | // any other widget on the screen. 294 | // 295 | // RectOp also takes the foreground and clicks events 296 | // and keyboard events will not be routed to Gio. 297 | // 298 | // Performance: changing the size/bounds or radius can 299 | // be expensive. If applicable, change the Rect, instead 300 | // of changing the size. 301 | // 302 | // Only one RectOp can be active at each frame for the 303 | // same WebViewOp. 304 | type RectOp struct { 305 | Size f32.Point 306 | SE, SW, NW, NE float32 307 | } 308 | 309 | // Rect creates a new RectOp. 310 | func Rect[POINT image.Point | f32.Point](v POINT) RectOp { 311 | switch v := any(v).(type) { 312 | case image.Point: 313 | return RectOp{Size: f32.Point{X: float32(v.X), Y: float32(v.Y)}} 314 | case f32.Point: 315 | return RectOp{Size: v} 316 | default: 317 | return RectOp{} 318 | } 319 | } 320 | 321 | var poolRectOp = newPool[RectOp](func() any { return new(RectOp) }) 322 | 323 | // Add adds a new RectOp to the queue. 324 | func (o RectOp) Add(op *op.Ops) { 325 | poolRectOp.add(op, o) 326 | } 327 | 328 | func (o *RectOp) execute(w *app.Window, p *plugin, e system.FrameEvent) { 329 | defer poolRectOp.free(o) 330 | p.seemMutex.Lock() 331 | defer p.seemMutex.Unlock() 332 | 333 | p.bounds[p.active][1].X += o.Size.X 334 | p.bounds[p.active][1].Y += o.Size.Y 335 | 336 | if _, ok := p.bounds[p.active]; !ok { 337 | p.bounds[p.active] = new([2]f32.Point) 338 | } 339 | 340 | p.bounds[p.active][0].X += float32(unit.Dp(e.Metric.PxPerDp) * e.Insets.Left) 341 | p.bounds[p.active][0].Y += float32(unit.Dp(e.Metric.PxPerDp) * e.Insets.Top) 342 | 343 | p.active.Resize( 344 | webview.Point{X: p.bounds[p.active][1].X, Y: p.bounds[p.active][1].Y}, 345 | webview.Point{X: p.bounds[p.active][0].X, Y: p.bounds[p.active][0].Y}, 346 | ) 347 | } 348 | 349 | // NavigateOp redirects the last Display to the 350 | // given URL. If the URL have unknown protocols, 351 | // or malformed URL may lead to unknown behaviors. 352 | type NavigateOp struct { 353 | // URL is the URL to redirect to. 354 | URL string 355 | } 356 | 357 | var poolNavigateOp = newPool[NavigateOp](func() any { return new(NavigateOp) }) 358 | 359 | // Add adds a new NavigateOp to the queue. 360 | func (o NavigateOp) Add(op *op.Ops) { 361 | poolNavigateOp.add(op, o) 362 | } 363 | 364 | func (o *NavigateOp) execute(w *app.Window, p *plugin, e system.FrameEvent) { 365 | defer poolNavigateOp.free(o) 366 | 367 | if e.Metric.PxPerDp != p.config.PxPerDp { 368 | p.config.PxPerDp = e.Metric.PxPerDp 369 | p.active.Configure(p.config) 370 | } 371 | 372 | u, err := url.Parse(o.URL) 373 | if err != nil { 374 | return 375 | } 376 | p.active.Navigate(u) 377 | } 378 | 379 | // SetCookieOp sets given cookie in the webview. 380 | type SetCookieOp struct { 381 | Cookie webview.CookieData 382 | } 383 | 384 | var poolSetCookieOp = newPool[SetCookieOp](func() any { return new(SetCookieOp) }) 385 | 386 | // Add adds a new SetCookieOp to the queue. 387 | func (o SetCookieOp) Add(op *op.Ops) { 388 | poolSetCookieOp.add(op, o) 389 | } 390 | 391 | func (o *SetCookieOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 392 | manager := p.active.DataManager() 393 | 394 | go func() { 395 | defer poolSetCookieOp.free(o) 396 | manager.AddCookie(o.Cookie) 397 | }() 398 | } 399 | 400 | // RemoveCookieOp sets given cookie in the webview. 401 | type RemoveCookieOp struct { 402 | Cookie webview.CookieData 403 | } 404 | 405 | var poolRemoveCookieOp = newPool[RemoveCookieOp](func() any { return new(RemoveCookieOp) }) 406 | 407 | // Add adds a new SetCookieOp to the queue. 408 | func (o RemoveCookieOp) Add(op *op.Ops) { 409 | poolRemoveCookieOp.add(op, o) 410 | } 411 | 412 | func (o *RemoveCookieOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 413 | manager := p.active.DataManager() 414 | 415 | go func() { 416 | defer poolRemoveCookieOp.free(o) 417 | manager.RemoveCookie(o.Cookie) 418 | }() 419 | } 420 | 421 | // ListCookieOp lists all cookies in the webview. 422 | // The response in sent via CookiesEvent using the 423 | // provided Tag. 424 | type ListCookieOp struct { 425 | Tag event.Tag 426 | } 427 | 428 | // CookiesEvent is the event sent when ListCookieOp is executed. 429 | type CookiesEvent struct { 430 | Cookies []webview.CookieData 431 | } 432 | 433 | // ImplementsEvent the event.Event interface. 434 | func (c CookiesEvent) ImplementsEvent() {} 435 | 436 | var poolListCookieOp = newPool[ListCookieOp](func() any { return new(ListCookieOp) }) 437 | 438 | // Add adds a new ListCookieOp to the queue. 439 | func (o ListCookieOp) Add(op *op.Ops) { 440 | poolListCookieOp.add(op, o) 441 | } 442 | 443 | func (o *ListCookieOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 444 | manager := p.active.DataManager() 445 | 446 | go func() { 447 | defer poolListCookieOp.free(o) 448 | evt := CookiesEvent{} 449 | manager.Cookies(func(c *webview.CookieData) bool { 450 | evt.Cookies = append(evt.Cookies, *c) 451 | return true 452 | }) 453 | p.queue.add(o.Tag, evt) 454 | }() 455 | } 456 | 457 | // StorageType is the type of storage. 458 | type StorageType int 459 | 460 | const ( 461 | // StorageTypeLocal is the local storage. 462 | StorageTypeLocal StorageType = iota 463 | // StorageTypeSession is the session storage. 464 | StorageTypeSession 465 | ) 466 | 467 | // SetStorageOp sets given Storage in the webview. 468 | type SetStorageOp struct { 469 | Local StorageType 470 | Content webview.StorageData 471 | } 472 | 473 | var poolSetStorageOp = newPool[SetStorageOp](func() any { return new(SetStorageOp) }) 474 | 475 | // Add adds a new SetStorageOp to the queue. 476 | func (o SetStorageOp) Add(op *op.Ops) { 477 | poolSetStorageOp.add(op, o) 478 | } 479 | 480 | func (o *SetStorageOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 481 | manager := p.active.DataManager() 482 | 483 | go func() { 484 | defer poolSetStorageOp.free(o) 485 | switch o.Local { 486 | case StorageTypeLocal: 487 | manager.AddLocalStorage(o.Content) 488 | case StorageTypeSession: 489 | manager.AddSessionStorage(o.Content) 490 | } 491 | }() 492 | } 493 | 494 | // RemoveStorageOp sets given Storage in the webview. 495 | type RemoveStorageOp struct { 496 | Local StorageType 497 | Content webview.StorageData 498 | } 499 | 500 | var poolRemoveStorageOp = newPool[RemoveStorageOp](func() any { return new(RemoveStorageOp) }) 501 | 502 | // Add adds a new SetStorageOp to the queue. 503 | func (o RemoveStorageOp) Add(op *op.Ops) { 504 | poolRemoveStorageOp.add(op, o) 505 | } 506 | 507 | func (o *RemoveStorageOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 508 | manager := p.active.DataManager() 509 | 510 | go func() { 511 | defer poolRemoveStorageOp.free(o) 512 | switch o.Local { 513 | case StorageTypeLocal: 514 | manager.RemoveLocalStorage(o.Content) 515 | case StorageTypeSession: 516 | manager.AddSessionStorage(o.Content) 517 | } 518 | }() 519 | } 520 | 521 | // ListStorageOp lists all Storage in the webview. 522 | // The response in sent via StorageEvent using the 523 | // provided Tag. 524 | type ListStorageOp struct { 525 | Local StorageType 526 | Tag event.Tag 527 | } 528 | 529 | // StorageEvent is the event sent when ListStorageOp is executed. 530 | type StorageEvent struct { 531 | Storage []webview.StorageData 532 | } 533 | 534 | // ImplementsEvent the event.Event interface. 535 | func (c StorageEvent) ImplementsEvent() {} 536 | 537 | var poolListStorageOp = newPool[ListStorageOp](func() any { return new(ListStorageOp) }) 538 | 539 | // Add adds a new ListStorageOp to the queue. 540 | func (o ListStorageOp) Add(op *op.Ops) { 541 | poolListStorageOp.add(op, o) 542 | } 543 | 544 | func (o *ListStorageOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 545 | manager := p.active.DataManager() 546 | 547 | go func() { 548 | defer poolListStorageOp.free(o) 549 | evt := StorageEvent{} 550 | 551 | fn := manager.LocalStorage 552 | if o.Local == StorageTypeSession { 553 | fn = manager.SessionStorage 554 | } 555 | 556 | fn(func(c *webview.StorageData) bool { 557 | evt.Storage = append(evt.Storage, *c) 558 | return true 559 | }) 560 | 561 | p.queue.add(o.Tag, evt) 562 | }() 563 | } 564 | 565 | // ExecuteJavascriptOp executes given JavaScript in the webview. 566 | type ExecuteJavascriptOp struct { 567 | Script string 568 | } 569 | 570 | var poolExecuteJavascript = newPool[ExecuteJavascriptOp](func() any { return new(ExecuteJavascriptOp) }) 571 | 572 | // Add adds a new ExecuteJavascriptOp to the queue. 573 | func (o ExecuteJavascriptOp) Add(op *op.Ops) { 574 | poolExecuteJavascript.add(op, o) 575 | } 576 | 577 | func (o *ExecuteJavascriptOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 578 | manager := p.active.JavascriptManager() 579 | 580 | go func() { 581 | defer poolExecuteJavascript.free(o) 582 | manager.RunJavaScript(o.Script) 583 | }() 584 | } 585 | 586 | // InstallJavascriptOp installs given JavaScript in the webview, executing 587 | // it every time the webview loads a new page. The script is executed before 588 | // the page is fully loaded. 589 | type InstallJavascriptOp struct { 590 | Script string 591 | } 592 | 593 | var poolInstallJavascript = newPool[InstallJavascriptOp](func() any { return new(InstallJavascriptOp) }) 594 | 595 | // Add adds a new ExecuteJavascriptOp to the queue. 596 | func (o InstallJavascriptOp) Add(op *op.Ops) { 597 | poolInstallJavascript.add(op, o) 598 | } 599 | 600 | func (o *InstallJavascriptOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 601 | manager := p.active.JavascriptManager() 602 | 603 | go func() { 604 | defer poolInstallJavascript.free(o) 605 | manager.InstallJavascript(o.Script, webview.JavascriptOnLoadStart) 606 | }() 607 | } 608 | 609 | // MessageReceiverOp receives a message from the webview, 610 | // and sends it to the provided Tag. The message is sent 611 | // as a string. 612 | // 613 | // You can use this to communicate with the webview, by using: 614 | // 615 | // window.callback.(); 616 | // 617 | // Consider that is the provided Name of the callback, 618 | // and is the message to send to Tag. 619 | // 620 | // For further information, see webview.JavascriptManager. 621 | type MessageReceiverOp struct { 622 | Name string 623 | Tag event.Tag 624 | } 625 | 626 | // MessageEvent is the event sent when receiving a message, 627 | // from previously defined MessageReceiverOp. 628 | type MessageEvent struct { 629 | Message string 630 | } 631 | 632 | // ImplementsEvent the event.Event interface. 633 | func (c MessageEvent) ImplementsEvent() {} 634 | 635 | var poolMessageReceiver = newPool[MessageReceiverOp](func() any { return new(MessageReceiverOp) }) 636 | 637 | // Add adds a new ExecuteJavascriptOp to the queue. 638 | func (o MessageReceiverOp) Add(op *op.Ops) { 639 | poolMessageReceiver.add(op, o) 640 | } 641 | 642 | func (o *MessageReceiverOp) execute(w *app.Window, p *plugin, _ system.FrameEvent) { 643 | defer poolMessageReceiver.free(o) 644 | 645 | p.active.JavascriptManager().AddCallback(o.Name, func(msg string) { 646 | p.queue.add(o.Tag, MessageEvent{Message: msg}) 647 | }) 648 | } 649 | 650 | // NavigationEvent is issued when the webview change the URL. 651 | type NavigationEvent webview.NavigationEvent 652 | 653 | // ImplementsEvent the event.Event interface. 654 | func (NavigationEvent) ImplementsEvent() {} 655 | 656 | // TitleEvent is issued when the webview change the title. 657 | type TitleEvent webview.TitleEvent 658 | 659 | // ImplementsEvent the event.Event interface. 660 | func (TitleEvent) ImplementsEvent() {} 661 | -------------------------------------------------------------------------------- /giowebview/giowebview_android.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "gioui.org/app" 5 | "git.wow.st/gmp/jni" 6 | "github.com/inkeliz/giowebview/webview" 7 | ) 8 | 9 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 10 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 11 | return webview.Config{View: jni.Class(evt.View), VM: jni.JVMFor(app.JavaVM()), Context: jni.Object(app.AppContext()), RunOnMain: w.Run} 12 | } 13 | -------------------------------------------------------------------------------- /giowebview/giowebview_ios.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "gioui.org/app" 5 | "github.com/inkeliz/giowebview/webview" 6 | ) 7 | 8 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 9 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 10 | return webview.Config{View: evt.ViewController, RunOnMain: w.Run} 11 | } 12 | -------------------------------------------------------------------------------- /giowebview/giowebview_js.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "syscall/js" 5 | 6 | "gioui.org/app" 7 | "github.com/inkeliz/giowebview/webview" 8 | "github.com/inkeliz/go_inkwasm/inkwasm" 9 | ) 10 | 11 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 12 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 13 | return webview.Config{Element: inkwasm.NewObjectFromSyscall(js.Global().Get("document").Get("body")), RunOnMain: w.Run} 14 | } 15 | -------------------------------------------------------------------------------- /giowebview/giowebview_macos.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && !ios 2 | 3 | package giowebview 4 | 5 | import ( 6 | "gioui.org/app" 7 | "github.com/inkeliz/giowebview/webview" 8 | ) 9 | 10 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 11 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 12 | return webview.Config{View: evt.View, Layer: evt.Layer, RunOnMain: w.Run} 13 | } 14 | -------------------------------------------------------------------------------- /giowebview/giowebview_unsupported.go: -------------------------------------------------------------------------------- 1 | //go:build !android && !darwin && !ios && !windows && !(js && wasm) 2 | 3 | package giowebview 4 | 5 | import ( 6 | "gioui.org/app" 7 | "github.com/inkeliz/giowebview/webview" 8 | ) 9 | 10 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 11 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 12 | return webview.Config{} 13 | } 14 | -------------------------------------------------------------------------------- /giowebview/giowebview_windows.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "gioui.org/app" 5 | "github.com/inkeliz/giowebview/webview" 6 | ) 7 | 8 | // NewConfigFromViewEvent creates a webview.Config based on app.ViewEvent. 9 | func NewConfigFromViewEvent(w *app.Window, evt app.ViewEvent) webview.Config { 10 | return webview.Config{HWND: evt.HWND, RunOnMain: w.Run} 11 | } 12 | -------------------------------------------------------------------------------- /giowebview/lists.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "sync" 5 | "unsafe" 6 | 7 | "gioui.org/app" 8 | "github.com/inkeliz/giowebview/webview" 9 | ) 10 | 11 | var _WebViewList sync.Map // map[uintptr]*webview.WebView 12 | 13 | func getWebView(t uintptr) (webview.WebView, bool) { 14 | v, ok := _WebViewList.Load(t) 15 | if !ok { 16 | return nil, ok 17 | } 18 | return v.(webview.WebView), ok 19 | } 20 | 21 | func setWebView(t uintptr, v webview.WebView) { 22 | _WebViewList.Store(t, v) 23 | } 24 | 25 | func removeWebView(t *int64) { 26 | _WebViewList.Delete(uintptr(unsafe.Pointer(t))) 27 | } 28 | 29 | var _PluginList sync.Map // map[*app.Window]*plugin 30 | 31 | func getPlugin(t *app.Window) (*plugin, bool) { 32 | v, ok := _PluginList.Load(uintptr(unsafe.Pointer(t))) 33 | if !ok { 34 | return nil, ok 35 | } 36 | return v.(*plugin), ok 37 | } 38 | 39 | func setPlugin(t *app.Window, v *plugin) { 40 | _PluginList.Store(uintptr(unsafe.Pointer(t)), v) 41 | } 42 | 43 | func removePlugin(t *app.Window) { 44 | _PluginList.Delete(uintptr(unsafe.Pointer(t))) 45 | } 46 | -------------------------------------------------------------------------------- /giowebview/pool.go: -------------------------------------------------------------------------------- 1 | package giowebview 2 | 3 | import ( 4 | "sync" 5 | 6 | "gioui.org/io/pointer" 7 | "gioui.org/op" 8 | "gioui.org/op/clip" 9 | ) 10 | 11 | type pool[T any] struct { 12 | pool sync.Pool 13 | } 14 | 15 | func newPool[T any](fn func() any) pool[T] { 16 | return pool[T]{pool: sync.Pool{New: fn}} 17 | } 18 | 19 | func (x *pool[T]) add(op *op.Ops, data T) { 20 | cmd := x.pool.New() 21 | *cmd.(*T) = data 22 | 23 | defer clip.Rect{}.Push(op).Pop() 24 | pointer.InputOp{Tag: cmd}.Add(op) 25 | } 26 | 27 | func (x *pool[T]) free(data *T) (v T) { 28 | *data = v 29 | x.pool.Put(data) 30 | return v 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/inkeliz/giowebview 2 | 3 | go 1.18 4 | 5 | require ( 6 | gioui.org v0.0.0-20220628163331-e21c665e70ae 7 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 8 | github.com/inkeliz/go_inkwasm v0.0.0-20220301223554-15ded5907c56 9 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e 10 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f 11 | ) 12 | 13 | require ( 14 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect 15 | gioui.org/shader v1.0.6 // indirect 16 | github.com/benoitkugler/textlayout v0.1.1 // indirect 17 | github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12 // indirect 18 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b // indirect 19 | golang.org/x/exp v0.0.0-20210722180016-6781d3edade3 // indirect 20 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect 21 | golang.org/x/text v0.3.8 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3 h1:djFprmHZgrSepsHAIRMp5UJn3PzsoTg9drI+BDmif5Q= 2 | gioui.org v0.0.0-20220628163331-e21c665e70ae h1:s8Erm0/zVvi3Fbq0ijjPkRT04XxcGZWTxkxDwUBsxuQ= 3 | gioui.org v0.0.0-20220628163331-e21c665e70ae/go.mod h1:WHoHbUjH91BJS2xkfps2AhKxji+9o3xwfsphGsCBfnM= 4 | gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 5 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= 6 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 7 | gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y= 8 | gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= 9 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 h1:Ynp3h+TC8k1clvf45D28VFQlmy0bPx8M/MG5bB24Vj8= 10 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= 11 | github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= 12 | github.com/benoitkugler/textlayout v0.0.5/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8= 13 | github.com/benoitkugler/textlayout v0.1.1 h1:hizE/085xAeY8q7gwV00uHR2Q27KYB2g1HW+UacXl68= 14 | github.com/benoitkugler/textlayout v0.1.1/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= 15 | github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQjtD6K2SFe9/HvnIbJk= 16 | github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= 17 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 18 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 19 | github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12 h1:1bjaB/5IIicfKpP4k0s30T2WEw//Kh00zULa8DQ0cxA= 20 | github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12/go.mod h1:kDhBRTA/i3H46PVdhqcw26TdGSIj42TOKNWKY+Kipnw= 21 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b h1:WINlj3ANt+CVrO2B4NGDHRlPvEWZPxjhb7z+JKypwXI= 22 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b/go.mod h1:ZNYu5saGoMOqtkVH5T8onTwhzenDUVszI+5WFHJRaxQ= 23 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 24 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 25 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 26 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 27 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 28 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 29 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 30 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 31 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 32 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 33 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 34 | github.com/inkeliz/go_inkwasm v0.0.0-20220301223554-15ded5907c56 h1:JNqiL40gBlkouchMBj29vAY8F0fr9cx4WMCzWDa2LXc= 35 | github.com/inkeliz/go_inkwasm v0.0.0-20220301223554-15ded5907c56/go.mod h1:LPI3Qojj7OgTyc2R4RPB6BuMSgjoOXCObwnDzz1SOVk= 36 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= 37 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= 38 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 39 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 40 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 41 | github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= 42 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 43 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 44 | github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= 45 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 46 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 47 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 48 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 49 | golang.org/x/exp v0.0.0-20210722180016-6781d3edade3 h1:IlrJD2AM5p8JhN/wVny9jt6gJ9hut2VALhSeZ3SYluk= 50 | golang.org/x/exp v0.0.0-20210722180016-6781d3edade3/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= 51 | golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 52 | golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 53 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= 54 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 55 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 56 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 57 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 58 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 59 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 60 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 61 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 62 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 63 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 65 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 66 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 67 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 68 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 70 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 72 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 73 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 74 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 76 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 78 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 79 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 80 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 81 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 82 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 83 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 84 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 85 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= 86 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 87 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 88 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 89 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 90 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 91 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 92 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 93 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 94 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 95 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 96 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 97 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 98 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 99 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 100 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 101 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 102 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 103 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 104 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 105 | -------------------------------------------------------------------------------- /installview/install.go: -------------------------------------------------------------------------------- 1 | package installview 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | // Installer is the WebView installer. 11 | type Installer struct { 12 | Data []byte 13 | } 14 | 15 | // Install will install the Installer. 16 | // 17 | // That will vary between OSes. 18 | func (i *Installer) Install() error { 19 | return i.install() 20 | } 21 | 22 | // Download will download the Installer. 23 | // 24 | // The download link will vary between OSes. 25 | func Download(ctx context.Context, client *http.Client) (*Installer, error) { 26 | if client == nil { 27 | client = http.DefaultClient 28 | } 29 | 30 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, DownloadURL, nil) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | resp, err := client.Do(req) 36 | if err != nil { 37 | return nil, err 38 | } 39 | defer resp.Body.Close() 40 | 41 | if resp.StatusCode != http.StatusOK { 42 | return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 43 | } 44 | 45 | data, err := io.ReadAll(resp.Body) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return &Installer{Data: data}, nil 51 | } 52 | -------------------------------------------------------------------------------- /installview/install_windows.go: -------------------------------------------------------------------------------- 1 | package installview 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | ) 11 | 12 | var DownloadURL = `https://go.microsoft.com/fwlink/p/?LinkId=2124703` 13 | 14 | func (i *Installer) install() error { 15 | if i.Data == nil { 16 | return fmt.Errorf("no data") 17 | } 18 | 19 | randName := make([]byte, 32) 20 | rand.Read(randName) 21 | 22 | path := filepath.Join(os.TempDir(), hex.EncodeToString(randName)+".exe") 23 | 24 | file, err := os.Create(path) 25 | if err != nil { 26 | return err 27 | } 28 | defer func() { os.Remove(path) }() 29 | 30 | if _, err := file.Write(i.Data); err != nil { 31 | return err 32 | } 33 | 34 | if err := file.Close(); err != nil { 35 | return err 36 | } 37 | 38 | return exec.Command("powershell.exe", "-Command", "Start-Process cmd '/c "+path+" /install' -WindowStyle hidden -Wait").Run() 39 | } 40 | -------------------------------------------------------------------------------- /installview/install_windows_test.go: -------------------------------------------------------------------------------- 1 | package installview 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func TestInstaller_Install(t *testing.T) { 9 | installer, err := Download(context.Background(), nil) 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | 14 | if err = installer.Install(); err != nil { 15 | t.Fatal(err) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webview/config.go: -------------------------------------------------------------------------------- 1 | //go:build !android && !darwin && !ios && !windows && !(js && wasm) 2 | 3 | package webview 4 | 5 | // Config is the configuration for a WebView. 6 | // 7 | // Each OS contains their own settings and options, 8 | // check each config_* file for more details. 9 | type Config struct{} 10 | -------------------------------------------------------------------------------- /webview/config_android.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "git.wow.st/gmp/jni" 5 | ) 6 | 7 | // Config is the configuration for a WebView. 8 | type Config struct { 9 | // View is the Android View. 10 | View jni.Class 11 | 12 | // VM is the Java VM. 13 | VM jni.JVM 14 | 15 | // Context is the Android Context. 16 | Context jni.Object 17 | 18 | // RunOnMain is a function that runs on the main UI thread. 19 | RunOnMain func(f func()) 20 | 21 | // PxPerDp represents how many pixels per each dp. 22 | PxPerDp float32 23 | } 24 | -------------------------------------------------------------------------------- /webview/config_ios.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | // Config is the configuration for a WebView. 4 | type Config struct { 5 | // View is a CFTypeRef for the UIViewController for the window. 6 | View uintptr 7 | 8 | // RunOnMain is a function that runs on the main UI thread. 9 | RunOnMain func(f func()) 10 | 11 | // PxPerDp represents how many pixels per each dp. 12 | PxPerDp float32 13 | } 14 | -------------------------------------------------------------------------------- /webview/config_js.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "github.com/inkeliz/go_inkwasm/inkwasm" 5 | ) 6 | 7 | // Config is the configuration for a WebView. 8 | type Config struct { 9 | // Element is the parent element of the webview. 10 | Element inkwasm.Object 11 | 12 | // RunOnMain is a function that runs on the main UI thread. 13 | RunOnMain func(f func()) 14 | 15 | // PxPerDp represents how many pixels per each dp. 16 | PxPerDp float32 17 | } 18 | -------------------------------------------------------------------------------- /webview/config_macos.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && !ios 2 | 3 | package webview 4 | 5 | // Config is the configuration for a WebView. 6 | type Config struct { 7 | // View is a CFTypeRef for the NSView for the window. 8 | View uintptr 9 | 10 | // Layer is a CFTypeRef of the CALayer of View. 11 | Layer uintptr 12 | 13 | // RunOnMain is a function that runs on the main UI thread. 14 | RunOnMain func(f func()) 15 | 16 | // PxPerDp represents how many pixels per each dp. 17 | PxPerDp float32 18 | } 19 | -------------------------------------------------------------------------------- /webview/config_unsupported.go: -------------------------------------------------------------------------------- 1 | //go:build !android && !darwin && !ios && !windows && !(js && wasm) 2 | 3 | package webview 4 | 5 | // Config is the configuration for a WebView. 6 | type Config struct{} 7 | -------------------------------------------------------------------------------- /webview/config_windows.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | // Config is the configuration for a WebView. 4 | type Config struct { 5 | // HWND is the handle to the window. 6 | HWND uintptr 7 | 8 | // RunOnMain is a function that runs on the main UI thread. 9 | RunOnMain func(f func()) 10 | 11 | // PxPerDp represents how many pixels per each dp. 12 | PxPerDp float32 13 | } 14 | -------------------------------------------------------------------------------- /webview/error.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | // ErrNotInstalled is returned when the webview is not installed. 9 | // Usually when WebView2 is not installed, see installview package for 10 | // more information of how install it. 11 | ErrNotInstalled = errors.New("webview is not installed") 12 | // ErrInvalidURL is returned when the URL is invalid. 13 | ErrInvalidURL = errors.New("invalid URL") 14 | // ErrInvalidSize is returned when the size is invalid. 15 | ErrInvalidSize = errors.New("invalid size") 16 | // ErrInvalidAuthProxy is returned when the proxy is invalid. 17 | ErrInvalidAuthProxy = errors.New("proxy with user and password is not supported") 18 | // ErrInvalidProxy is returned when the proxy is invalid. 19 | ErrInvalidProxy = errors.New("invalid proxy") 20 | // ErrInvalidCert is returned when the certificate is invalid. 21 | ErrInvalidCert = errors.New("invalid certificate") 22 | // ErrInvalidOptionChange is returned when an option is changed after the webview is created. 23 | ErrInvalidOptionChange = errors.New("invalid option change") 24 | 25 | // ErrNotSupported is returned when a feature is not supported. 26 | ErrNotSupported = errors.New("feature not supported") 27 | 28 | // ErrJavascriptManagerNotSupported is returned when the javascript manager is not supported. 29 | ErrJavascriptManagerNotSupported = errors.New("javascript manager not supported") 30 | // ErrJavascriptCallbackDuplicate is returned when the javascript callback is duplicated. 31 | ErrJavascriptCallbackDuplicate = errors.New("javascript callback is duplicated") 32 | // ErrJavascriptCallbackInvalidName is returned when the javascript name is too long. 33 | ErrJavascriptCallbackInvalidName = errors.New("javascript name is too long") 34 | // ErrInvalidJavascript is returned when the javascript is invalid. 35 | ErrInvalidJavascript = errors.New("invalid javascript") 36 | ) 37 | -------------------------------------------------------------------------------- /webview/events.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | // Event is the marker interface for events. 4 | type Event interface { 5 | ImplementsEvent() 6 | } 7 | 8 | // NavigationEvent is issued when the URL is changed 9 | // in the WebView. 10 | type NavigationEvent struct { 11 | URL string 12 | } 13 | 14 | func (NavigationEvent) ImplementsEvent() {} 15 | 16 | // TitleEvent is issued when the Title of the website 17 | // is changed in the WebView. 18 | type TitleEvent struct { 19 | Title string 20 | } 21 | 22 | func (TitleEvent) ImplementsEvent() {} 23 | -------------------------------------------------------------------------------- /webview/internal/fan.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Fan is a fan-out channel. 8 | type Fan[T any] struct { 9 | mutex sync.Mutex 10 | listeners []chan T 11 | } 12 | 13 | // Send sends a value to all listeners. 14 | func (f *Fan[T]) Send(v T) { 15 | f.mutex.Lock() 16 | defer f.mutex.Unlock() 17 | 18 | if len(f.listeners) == 0 { 19 | return 20 | } 21 | 22 | for _, c := range f.listeners { 23 | c <- v 24 | } 25 | } 26 | 27 | // Add adds a listener to the fan-out channel. 28 | func (f *Fan[T]) Add() chan T { 29 | f.mutex.Lock() 30 | defer f.mutex.Unlock() 31 | 32 | c := make(chan T) 33 | f.listeners = append(f.listeners, c) 34 | return c 35 | } 36 | 37 | // Close removes a listener from the fan-out channel. 38 | func (f *Fan[T]) Close() { 39 | for _, c := range f.listeners { 40 | close(c) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webview/internal/handle.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "crypto/rand" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | var ( 10 | key = [32]byte{} 11 | handles = sync.Map{} // map[Handle]interface{} 12 | handleIdx uintptr // atomic 13 | ) 14 | 15 | func init() { 16 | rand.Read(key[:]) 17 | } 18 | 19 | type Handle uintptr 20 | 21 | // NewHandle returns a handle for a given value. 22 | func NewHandle(v any) Handle { 23 | h := atomic.AddUintptr(&handleIdx, 1) 24 | if h == 0 { 25 | panic("ran out of handle space") 26 | } 27 | 28 | handles.Store(h, v) 29 | return Handle(h) 30 | } 31 | 32 | // Value returns the associated Go value for a valid handle. 33 | // 34 | // The method panics if the handle is invalid. 35 | func (h Handle) Value() any { 36 | v, ok := handles.Load(uintptr(h)) 37 | if !ok { 38 | panic("misuse of an invalid Handle") 39 | } 40 | return v 41 | } 42 | 43 | // IsValid returns true if the handle is valid. 44 | func (h Handle) IsValid() bool { 45 | _, ok := handles.Load(uintptr(h)) 46 | return ok 47 | } 48 | 49 | // Delete invalidates a handle. 50 | func (h Handle) Delete() { 51 | if _, ok := handles.LoadAndDelete(uintptr(h)); !ok { 52 | panic("misuse of an invalid Handle") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /webview/internal/scheduler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Scheduler is a runs a set of functions in the given an 8 | // single runner function. 9 | // 10 | // That is useful for running a set of functions in a single 11 | // goroutine, linked to the main loop. 12 | type Scheduler struct { 13 | runner func(f func()) 14 | 15 | mutex sync.Mutex 16 | counter int 17 | tasks map[int]func() 18 | queue []func() 19 | update chan struct{} 20 | } 21 | 22 | // NewScheduler creates a new Scheduler. 23 | func NewScheduler(runner func(f func())) (s *Scheduler) { 24 | s = &Scheduler{} 25 | s.SetRunner(runner) 26 | return s 27 | } 28 | 29 | // SetRunner sets the runner function. 30 | func (s *Scheduler) SetRunner(r func(f func())) { 31 | s.mutex.Lock() 32 | defer s.mutex.Unlock() 33 | 34 | if r == nil { 35 | return 36 | } 37 | 38 | if s.runner == nil { 39 | if s.update == nil { 40 | s.update = make(chan struct{}, 1) 41 | } 42 | 43 | go func() { 44 | fns := make([]func(), 0, 32) 45 | last := 0 46 | for range s.update { 47 | s.mutex.Lock() 48 | if last == s.counter { 49 | s.mutex.Unlock() 50 | continue 51 | } 52 | 53 | last = s.counter 54 | for i := range s.tasks { 55 | if s.tasks[i] != nil { 56 | fns = append(fns, s.tasks[i]) 57 | } 58 | s.tasks[i] = nil 59 | } 60 | for i := range s.queue { 61 | fns = append(fns, s.queue[i]) 62 | } 63 | s.queue = s.queue[:0] 64 | 65 | runner := s.runner 66 | s.mutex.Unlock() 67 | 68 | for i := range fns { 69 | runner(fns[i]) 70 | } 71 | 72 | fns = fns[:0] 73 | 74 | s.mutex.Lock() 75 | if s.counter != last && len(s.update) == 0 { 76 | s.update <- struct{}{} 77 | } 78 | s.mutex.Unlock() 79 | } 80 | }() 81 | } 82 | 83 | s.runner = r 84 | s.signal() 85 | } 86 | 87 | // Run runs a function in the scheduler, however it only 88 | // allow one function per method. 89 | // 90 | // That will replace the old declared function if the 91 | // method is already in use. 92 | func (s *Scheduler) Run(method int, f func()) { 93 | s.mutex.Lock() 94 | defer s.mutex.Unlock() 95 | 96 | if s.tasks == nil { 97 | s.tasks = make(map[int]func(), 32) 98 | } 99 | 100 | s.tasks[method] = f 101 | s.counter++ 102 | 103 | s.signal() 104 | } 105 | 106 | // MustRun runs a function in the scheduler, that is 107 | // guaranteed to run and duplication may happen if 108 | // misused. 109 | func (s *Scheduler) MustRun(f func()) { 110 | s.mutex.Lock() 111 | defer s.mutex.Unlock() 112 | 113 | s.counter++ 114 | s.queue = append(s.queue, f) 115 | 116 | s.signal() 117 | } 118 | 119 | func (s *Scheduler) signal() { 120 | select { 121 | case s.update <- struct{}{}: 122 | default: 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /webview/javascript.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/inkeliz/giowebview/webview/internal" 7 | ) 8 | 9 | type JavascriptManager interface { 10 | // RunJavaScript execute the given JavaScript code into the loaded page. 11 | // 12 | // The behavior of this function is undefined if the page is not loaded. 13 | RunJavaScript(js string) error 14 | 15 | // InstallJavascript installs the given JavaScript code as a function, 16 | // that will be injected into the page when it is loaded. 17 | // 18 | // The behavior of this function is undefined if the page is already loaded. 19 | InstallJavascript(js string, when JavascriptInstallationTime) error 20 | 21 | // AddCallback registers a callback for the given JavaScript function. 22 | // 23 | // The function can be called as follows: 24 | // 25 | // window.callback.(); 26 | // 27 | // The function will be called with the message provided by the JavaScript caller, 28 | // you probably want to encode (in JSON/Protobuf/Flatbuffers/Karmem/CapNProto or 29 | // another more efficient format). Due to different browser implementations and memory layouts, it's not possible to 30 | // pass a pointer to a struct to the JavaScript function or another type rather than 31 | // a string. 32 | // 33 | // It may introduce performance penalties, so use it wisely. In order to be compatible 34 | // with multiples webviews and OSes, it may introduce more indirection and use ProxyAPI 35 | // and ReflectionAPI and other slow APIs to achieve the same functionality 36 | // 37 | // If you want to return a result to the JavaScript caller, use RunJavaScript and 38 | // set the result into some array or global variable. 39 | // 40 | // The name must be unique and must not contain any dots, and must have a maximum 41 | // length of 255 characters. 42 | AddCallback(name string, fn func(message string)) error 43 | } 44 | 45 | // JavascriptInstallationTime defines when the JavaScript code is injected. 46 | type JavascriptInstallationTime int64 47 | 48 | const ( 49 | // JavascriptOnLoadStart ensures that the JavaScript code is injected on page load start, 50 | // before the page is fully loaded. 51 | JavascriptOnLoadStart JavascriptInstallationTime = iota 52 | 53 | // JavascriptOnLoadFinish ensures that the JavaScript code is injected on page load end, when 54 | // all contents are loaded. 55 | JavascriptOnLoadFinish 56 | ) 57 | 58 | // scriptCallback uses ProxyAPI to provide the function, which is called by JS. 59 | // The "%s" is replaced by the native function, which varies across each OS 60 | // implementation. 61 | var scriptCallback = ` 62 | globalThis.callback = new Proxy({}, { 63 | get(self, name) { 64 | return function(message) { 65 | let size = (name.toString().length).toString(16).toUpperCase().padStart(2, '0'); 66 | %s(size + name.toString() + message); 67 | } 68 | }, 69 | }); 70 | ` 71 | 72 | func receiveCallback(handler uintptr, in string) { 73 | if len(in) < 3 { 74 | return 75 | } 76 | 77 | size, err := strconv.ParseUint(in[:2], 16, 8) 78 | if err != nil { 79 | return 80 | } 81 | 82 | name := in[2 : 2+size] 83 | message := in[2+size:] 84 | 85 | j := internal.Handle(handler).Value().(*javascriptManager) 86 | if fn, ok := j.callbacks.Load(name); ok { 87 | fn.(func(message string))(message) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /webview/javascript_android.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | /* 4 | #cgo CFLAGS: -Werror 5 | #cgo LDFLAGS: -landroid 6 | 7 | #include 8 | #include 9 | */ 10 | import "C" 11 | import ( 12 | "fmt" 13 | "strings" 14 | "sync" 15 | "unsafe" 16 | 17 | "git.wow.st/gmp/jni" 18 | "github.com/inkeliz/giowebview/webview/internal" 19 | ) 20 | 21 | type javascriptManager struct { 22 | webview *webview 23 | jsHandler internal.Handle 24 | callbacks sync.Map // map[string]func(message string) 25 | } 26 | 27 | func newJavascriptManager(w *webview) *javascriptManager { 28 | r := &javascriptManager{webview: w} 29 | r.jsHandler = internal.NewHandle(r) 30 | r.installCallback() 31 | r.installJavascript(fmt.Sprintf(scriptCallback, `_callback.callback`), JavascriptOnLoadStart) 32 | return r 33 | } 34 | 35 | func (j *javascriptManager) installCallback() { 36 | j.webview.driver.callArgs("webview_set_callback", "(J)V", func(env jni.Env) []jni.Value { 37 | return []jni.Value{jni.Value(j.jsHandler)} 38 | }) 39 | } 40 | 41 | func (j *javascriptManager) RunJavaScript(js string) error { 42 | done := make(chan error) 43 | dr := internal.NewHandle(done) 44 | defer dr.Delete() 45 | 46 | j.webview.scheduler.MustRun(func() { 47 | j.webview.driver.callArgs("webview_run_javascript", "(Ljava/lang/String;J)V", func(env jni.Env) []jni.Value { 48 | return []jni.Value{ 49 | jni.Value(jni.JavaString(env, js)), 50 | jni.Value(int64(dr)), 51 | } 52 | }) 53 | }) 54 | 55 | return <-done 56 | } 57 | 58 | func (j *javascriptManager) InstallJavascript(js string, when JavascriptInstallationTime) (err error) { 59 | j.webview.scheduler.MustRun(func() { 60 | err = j.installJavascript(js, when) 61 | }) 62 | return err 63 | } 64 | 65 | func (j *javascriptManager) installJavascript(js string, when JavascriptInstallationTime) error { 66 | done := make(chan error) 67 | dr := internal.NewHandle(done) 68 | defer dr.Delete() 69 | 70 | j.webview.driver.callArgs("webview_install_javascript", "(Ljava/lang/String;JJ)V", func(env jni.Env) []jni.Value { 71 | return []jni.Value{ 72 | jni.Value(jni.JavaString(env, js)), 73 | jni.Value(when), 74 | jni.Value(int64(dr)), 75 | } 76 | }) 77 | 78 | return <-done 79 | } 80 | 81 | func (j *javascriptManager) AddCallback(name string, fn func(message string)) error { 82 | if len(name) > 255 { 83 | return ErrJavascriptCallbackInvalidName 84 | } 85 | if strings.Contains(name, ".") || strings.Contains(name, " ") { 86 | return ErrJavascriptCallbackInvalidName 87 | } 88 | if _, ok := j.callbacks.Load(name); ok { 89 | return ErrJavascriptCallbackDuplicate 90 | } 91 | 92 | j.callbacks.Store(name, fn) 93 | return nil 94 | } 95 | 96 | //export Java_com_inkeliz_webview_sys_1android_sendCallback 97 | func Java_com_inkeliz_webview_sys_1android_sendCallback(env *C.JNIEnv, class C.jclass, ptr C.jlong, msg C.jstring) { 98 | receiveCallback(uintptr(C.jlong(ptr)), jni.GoString(jni.EnvFor(uintptr(unsafe.Pointer(env))), jni.String(msg))) 99 | } 100 | -------------------------------------------------------------------------------- /webview/javascript_darwin.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | /* 4 | #cgo CFLAGS: -xobjective-c -fmodules -fobjc-arc 5 | 6 | #include 7 | #import 8 | 9 | void addCallbackJavascript(CFTypeRef config, char *name, uintptr_t handler); 10 | void runJavascript(CFTypeRef web, char *js, uintptr_t done); 11 | void installJavascript(CFTypeRef config, char *js, uint64_t when); 12 | 13 | */ 14 | import "C" 15 | import ( 16 | "fmt" 17 | "strings" 18 | "sync" 19 | "unsafe" 20 | 21 | "github.com/inkeliz/giowebview/webview/internal" 22 | ) 23 | 24 | type javascriptManager struct { 25 | webview *webview 26 | jsHandler internal.Handle 27 | callbacks sync.Map // map[string]func(message string) 28 | } 29 | 30 | func newJavascriptManager(w *webview) *javascriptManager { 31 | r := &javascriptManager{webview: w} 32 | r.installJavascript(fmt.Sprintf(scriptCallback, `window.webkit.messageHandlers.callback.postMessage`), JavascriptOnLoadStart) 33 | 34 | r.jsHandler = internal.NewHandle(r) 35 | 36 | name := C.CString("callback") 37 | defer C.free(unsafe.Pointer(name)) 38 | 39 | C.addCallbackJavascript(r.webview.driver.webviewConfig, name, C.uintptr_t(r.jsHandler)) 40 | 41 | return r 42 | } 43 | 44 | //export javascriptManagerCallback 45 | func javascriptManagerCallback(handler uintptr, input *C.char) { 46 | receiveCallback(handler, C.GoString(input)) 47 | } 48 | 49 | // RunJavaScript implements the JavascriptManager interface. 50 | func (j *javascriptManager) RunJavaScript(js string) error { 51 | done := make(chan error) 52 | 53 | dr := internal.NewHandle(done) 54 | defer dr.Delete() 55 | 56 | j.webview.scheduler.MustRun(func() { 57 | C.runJavascript(j.webview.driver.webviewObject, C.CString(js), C.uintptr_t(dr)) 58 | }) 59 | 60 | <-done 61 | return nil 62 | } 63 | 64 | // InstallJavascript implements the JavascriptManager interface. 65 | func (j *javascriptManager) InstallJavascript(js string, when JavascriptInstallationTime) error { 66 | j.webview.scheduler.MustRun(func() { 67 | j.installJavascript(js, when) 68 | }) 69 | return nil 70 | } 71 | 72 | func (j *javascriptManager) installJavascript(js string, when JavascriptInstallationTime) { 73 | code := C.CString(js) 74 | defer C.free(unsafe.Pointer(code)) 75 | 76 | C.installJavascript(j.webview.driver.webviewConfig, code, C.uint64_t(when)) 77 | } 78 | 79 | // AddCallback implements the JavascriptManager interface. 80 | func (j *javascriptManager) AddCallback(name string, fn func(message string)) error { 81 | if len(name) > 255 { 82 | return ErrJavascriptCallbackInvalidName 83 | } 84 | if strings.Contains(name, ".") || strings.Contains(name, " ") { 85 | return ErrJavascriptCallbackInvalidName 86 | } 87 | if _, ok := j.callbacks.Load(name); ok { 88 | return ErrJavascriptCallbackDuplicate 89 | } 90 | 91 | j.callbacks.Store(name, fn) 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /webview/javascript_js.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type javascriptManager struct { 8 | callbacks sync.Map 9 | } 10 | 11 | func newJavascriptManager(w *webview) *javascriptManager { 12 | return new(javascriptManager) 13 | } 14 | 15 | // RunJavaScript implements the JavascriptManager interface. 16 | func (*javascriptManager) RunJavaScript(_ string) error { 17 | return ErrNotSupported 18 | } 19 | 20 | // InstallJavascript implements the JavascriptManager interface. 21 | func (*javascriptManager) InstallJavascript(_ string, _ JavascriptInstallationTime) error { 22 | return ErrNotSupported 23 | } 24 | 25 | // AddCallback implements the JavascriptManager interface. 26 | func (*javascriptManager) AddCallback(_ string, _ func(message string)) error { 27 | return ErrNotSupported 28 | } 29 | -------------------------------------------------------------------------------- /webview/javascript_windows.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "syscall" 8 | "unsafe" 9 | 10 | "github.com/inkeliz/giowebview/webview/internal" 11 | "golang.org/x/sys/windows" 12 | ) 13 | 14 | type javascriptManager struct { 15 | webview *webview 16 | jsHandler internal.Handle 17 | callbacks sync.Map // map[string]func(message string) 18 | callback *_ICoreWebView2FrameWebMessageReceivedEventHandler 19 | } 20 | 21 | func newJavascriptManager(w *webview) *javascriptManager { 22 | r := &javascriptManager{webview: w} 23 | r.jsHandler = internal.NewHandle(r) 24 | w.scheduler.MustRun(func() { 25 | r.installCallback() 26 | r.installJavascript(fmt.Sprintf(scriptCallback, `window.chrome.webview.postMessage`)) 27 | }) 28 | return r 29 | } 30 | 31 | func (j *javascriptManager) installCallback() { 32 | j.callback = &_ICoreWebView2FrameWebMessageReceivedEventHandler{ 33 | VTBL: _CoreWebView2FrameWebMessageReceivedEventHandlerVTBL, 34 | Invoke: func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler, frame uintptr, args *_ICoreWebView2WebMessageReceivedEventArgs) uintptr { 35 | var message *uint16 36 | syscall.SyscallN( 37 | args.VTBL.TryGetWebMessageAsString, 38 | uintptr(unsafe.Pointer(args)), 39 | uintptr(unsafe.Pointer(&message)), 40 | ) 41 | if message != nil { 42 | receiveCallback(uintptr(j.jsHandler), windows.UTF16PtrToString(message)) 43 | } 44 | return 0 45 | }, 46 | } 47 | 48 | j.webview.scheduler.MustRun(func() { 49 | var r uint64 50 | syscall.SyscallN( 51 | j.webview.driver.webview2.VTBL.AddWebMessageReceived, 52 | uintptr(unsafe.Pointer(j.webview.driver.webview2)), 53 | uintptr(unsafe.Pointer(j.callback)), 54 | uintptr(unsafe.Pointer(&r)), 55 | ) 56 | }) 57 | } 58 | 59 | // RunJavaScript implements the JavascriptManager interface. 60 | func (j *javascriptManager) RunJavaScript(js string) error { 61 | done := make(chan error) 62 | fr := internal.NewHandle(done) 63 | defer fr.Delete() 64 | 65 | for _, c := range js { 66 | if c == 0x00 { 67 | return ErrInvalidJavascript 68 | } 69 | } 70 | 71 | handler := &_ICoreWebView2ExecuteScriptCompletedHandler{ 72 | VTBL: _CoreWebView2ExecuteScriptCompletedHandlerVTBL, 73 | Invoke: func(this *_ICoreWebView2ExecuteScriptCompletedHandler, err uintptr, resulAsJson uintptr) uintptr { 74 | done <- nil 75 | return 0 76 | }, 77 | } 78 | 79 | text := syscall.StringToUTF16Ptr(js) 80 | 81 | j.webview.scheduler.MustRun(func() { 82 | syscall.SyscallN( 83 | j.webview.driver.webview2.VTBL.ExecuteScript, 84 | uintptr(unsafe.Pointer(j.webview.driver.webview2)), 85 | uintptr(unsafe.Pointer(text)), 86 | uintptr(unsafe.Pointer(handler)), 87 | ) 88 | }) 89 | 90 | return <-done 91 | } 92 | 93 | // InstallJavascript implements the JavascriptManager interface. 94 | func (j *javascriptManager) InstallJavascript(js string, when JavascriptInstallationTime) error { 95 | if when == JavascriptOnLoadFinish { 96 | js = fmt.Sprintf(`document.addEventListener('DOMContentLoaded', function() { %s };`, js) 97 | } 98 | 99 | j.webview.scheduler.MustRun(func() { 100 | j.installJavascript(js) 101 | }) 102 | 103 | return nil 104 | } 105 | 106 | func (j *javascriptManager) installJavascript(js string) { 107 | syscall.SyscallN( 108 | j.webview.driver.webview2.VTBL.AddScriptToExecuteOnDocumentCreated, 109 | uintptr(unsafe.Pointer(j.webview.driver.webview2)), 110 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(js))), 111 | ) 112 | } 113 | 114 | // AddCallback implements the JavascriptManager interface. 115 | func (j *javascriptManager) AddCallback(name string, fn func(message string)) error { 116 | if len(name) > 255 { 117 | return ErrJavascriptCallbackInvalidName 118 | } 119 | if strings.Contains(name, ".") || strings.Contains(name, " ") { 120 | return ErrJavascriptCallbackInvalidName 121 | } 122 | if _, ok := j.callbacks.Load(name); ok { 123 | return ErrJavascriptCallbackDuplicate 124 | } 125 | 126 | j.callbacks.Store(name, fn) 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /webview/options.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "crypto/x509" 5 | "net/url" 6 | "sync" 7 | ) 8 | 9 | var options = struct { 10 | sync.Mutex 11 | proxy struct{ ip, port string } 12 | certs []*x509.Certificate 13 | }{} 14 | 15 | // SetProxy sets the HTTP proxy to use for the webview. 16 | // It's applied for all viewers, and must be called before creating any WebView. 17 | // 18 | // Only supported by Windows and Android. 19 | func SetProxy(u *url.URL) error { 20 | options.Lock() 21 | defer options.Unlock() 22 | 23 | if options.proxy.ip != "" && options.proxy.port != "" { 24 | return ErrInvalidOptionChange 25 | } 26 | 27 | if u.User != nil { 28 | return ErrInvalidAuthProxy 29 | } 30 | 31 | options.proxy.ip = u.Hostname() 32 | options.proxy.port = u.Port() 33 | 34 | return nil 35 | } 36 | 37 | // SetCustomCertificates sets additional certificates to use for the webview. 38 | // It's applied for all viewers, and must be called before creating any WebView. 39 | // 40 | // Only supported by Windows and Android. 41 | func SetCustomCertificates(certs []*x509.Certificate) error { 42 | options.Lock() 43 | defer options.Unlock() 44 | 45 | if options.certs != nil { 46 | return ErrInvalidOptionChange 47 | } 48 | 49 | options.certs = certs 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /webview/options_android.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | "git.wow.st/gmp/jni" 7 | ) 8 | 9 | func (r *driver) setProxy() error { 10 | options.Lock() 11 | defer options.Unlock() 12 | 13 | if options.proxy.ip == "" && options.proxy.port == "" { 14 | return nil 15 | } 16 | 17 | ok, err := r.callBooleanArgs("webview_proxy", "(Ljava/lang/String;Ljava/lang/String;)Z", func(env jni.Env) []jni.Value { 18 | return []jni.Value{ 19 | jni.Value(jni.JavaString(env, options.proxy.ip)), 20 | jni.Value(jni.JavaString(env, options.proxy.port)), 21 | } 22 | }) 23 | 24 | if err != nil || !ok { 25 | return ErrInvalidProxy 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func (r *driver) setCerts() error { 32 | options.Lock() 33 | defer options.Unlock() 34 | 35 | if options.certs == nil { 36 | return nil 37 | } 38 | 39 | var jcerts string 40 | for _, c := range options.certs { 41 | jcerts += base64.StdEncoding.EncodeToString(c.Raw) + ";" 42 | } 43 | 44 | ok, err := r.callBooleanArgs("webview_certs", "(Ljava/lang/String;)Z", func(env jni.Env) []jni.Value { 45 | return []jni.Value{ 46 | jni.Value(jni.JavaString(env, jcerts)), 47 | } 48 | }) 49 | 50 | if err != nil || !ok { 51 | return ErrInvalidCert 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /webview/options_windows.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/base64" 6 | "fmt" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func (r *driver) setProxy() { 12 | options.Lock() 13 | defer options.Unlock() 14 | 15 | if options.proxy.ip == "" && options.proxy.port == "" { 16 | return 17 | } 18 | 19 | proxy := fmt.Sprintf("%s:%s", options.proxy.ip, options.proxy.port) 20 | if strings.Index(options.proxy.ip, `:`) >= 0 && !strings.HasPrefix(options.proxy.ip, `[`) && !strings.HasPrefix(options.proxy.ip, `]`) { 21 | proxy = fmt.Sprintf("[%s]:%s", options.proxy.ip, options.proxy.port) 22 | } 23 | 24 | os.Setenv("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", 25 | fmt.Sprintf(`%s --proxy-server="%s"`, os.Getenv("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"), proxy), 26 | ) 27 | } 28 | 29 | func (r *driver) setCerts() { 30 | options.Lock() 31 | defer options.Unlock() 32 | 33 | if options.certs == nil { 34 | return 35 | } 36 | 37 | var jcerts string 38 | h := sha256.New() 39 | for _, c := range options.certs { 40 | h.Write(c.RawSubjectPublicKeyInfo) 41 | jcerts += base64.StdEncoding.EncodeToString(h.Sum(nil)) + "," 42 | h.Reset() 43 | } 44 | 45 | os.Setenv("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", 46 | fmt.Sprintf(`%s --ignore-certificate-errors-spki-list="%s"`, os.Getenv("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"), jcerts), 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /webview/storage.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // DataManager is a data manager for the webview. 8 | type DataManager interface { 9 | CookieManager 10 | StorageManager 11 | } 12 | 13 | // CookieManager can access and modify cookies from Webview. 14 | // 15 | // Cookies might be shared between Webview instances, under 16 | // the same app or device. 17 | type CookieManager interface { 18 | // Cookies returns the cookies for the current page or all browser. 19 | Cookies(fn DataLooper[CookieData]) (err error) 20 | // AddCookie adds a cookie/local-storage item. 21 | // The cookie must be a valid cookie string, without semi-colon, space, or comma. 22 | AddCookie(c CookieData) error 23 | // RemoveCookie removes a cookie/local-storage item. 24 | RemoveCookie(c CookieData) error 25 | } 26 | 27 | // StorageManager can access and modify LocalStorage/SessionStorage and other storage devices from Webview. 28 | type StorageManager interface { 29 | // LocalStorage returns the local storage for the current page. 30 | LocalStorage(fn DataLooper[StorageData]) (err error) 31 | // AddLocalStorage adds a local storage item. 32 | AddLocalStorage(c StorageData) error 33 | // RemoveLocalStorage removes a local storage item. 34 | RemoveLocalStorage(c StorageData) error 35 | 36 | // SessionStorage returns the session storage for the current page. 37 | SessionStorage(fn DataLooper[StorageData]) (err error) 38 | // AddSessionStorage adds a session storage item. 39 | AddSessionStorage(c StorageData) error 40 | // RemoveSessionStorage removes a session storage item. 41 | RemoveSessionStorage(c StorageData) error 42 | } 43 | 44 | // DataLooper receives one or more data chunks. 45 | // The pointer to the data is valid until the next call to DataLooper, 46 | // you must use/copy it before the end of the call. 47 | // 48 | // The function must return true to continue receiving data. 49 | type DataLooper[T CookieData | StorageData] func(d *T) (next bool) 50 | 51 | // StorageData is a LocalStorage data. 52 | type StorageData struct { 53 | Key string 54 | Value string 55 | } 56 | 57 | // CookieData is a cookie data. 58 | type CookieData struct { 59 | Name string 60 | Value string 61 | Domain string 62 | Path string 63 | Expires time.Time 64 | Features CookieFeatures 65 | } 66 | 67 | // CookieFeatures is a set of cookie features. 68 | type CookieFeatures uint64 69 | 70 | const ( 71 | // CookieSecure is a secure cookie. 72 | CookieSecure CookieFeatures = 1 << iota 73 | // CookieHTTPOnly is a http only cookie. 74 | CookieHTTPOnly 75 | ) 76 | 77 | // IsSecure returns true if the cookie is secure. 78 | func (f CookieFeatures) IsSecure() bool { return f&CookieSecure == CookieSecure } 79 | 80 | // IsHTTPOnly returns true if the cookie is http only. 81 | func (f CookieFeatures) IsHTTPOnly() bool { return f&CookieHTTPOnly == CookieHTTPOnly } 82 | -------------------------------------------------------------------------------- /webview/storage_all.go: -------------------------------------------------------------------------------- 1 | //go:build ios || darwin || windows || js || android 2 | 3 | package webview 4 | 5 | import ( 6 | "strconv" 7 | 8 | "github.com/inkeliz/giowebview/webview/internal" 9 | ) 10 | 11 | type dataManager struct { 12 | CookieManager 13 | StorageManager 14 | } 15 | 16 | func newDataManager(w *webview) DataManager { 17 | return &dataManager{ 18 | CookieManager: newCookieManager(w), 19 | StorageManager: newStorageManager(w), 20 | } 21 | } 22 | 23 | type storageManager struct { 24 | WebView 25 | } 26 | 27 | func newStorageManager(webview WebView) *storageManager { 28 | r := &storageManager{WebView: webview} 29 | r.JavascriptManager().AddCallback("_sendStorage", _getStorage) 30 | 31 | return r 32 | } 33 | 34 | func _getStorage(msg string) { 35 | if len(msg) < 48 { 36 | return 37 | } 38 | 39 | fr, err := strconv.ParseUint(msg[:16], 16, 64) 40 | if err != nil || !internal.Handle(fr).IsValid() { 41 | return 42 | } 43 | ksize, err := strconv.ParseUint(msg[16:32], 16, 64) 44 | if err != nil { 45 | return 46 | } 47 | 48 | vsize, err := strconv.ParseUint(msg[32:48], 16, 64) 49 | if err != nil { 50 | return 51 | } 52 | 53 | if len(msg) < int(48+ksize+vsize) { 54 | return 55 | } 56 | 57 | internal.Handle(fr).Value().(DataLooper[StorageData])(&StorageData{ 58 | Key: msg[48 : 48+ksize], 59 | Value: msg[48+ksize : 48+ksize+vsize], 60 | }) 61 | } 62 | 63 | // LocalStorage implements the StorageManager interface. 64 | func (s *storageManager) LocalStorage(fn DataLooper[StorageData]) (err error) { 65 | fr := internal.NewHandle(fn) 66 | defer fr.Delete() 67 | 68 | return s.JavascriptManager().RunJavaScript(` 69 | (function() { 70 | let fr = "` + strconv.FormatUint(uint64(fr), 16) + `".padStart(16, '0'); 71 | for (i = 0; i < globalThis.localStorage.length; i++) { 72 | let key = globalThis.localStorage.key(i); 73 | let value = globalThis.localStorage.getItem(key); 74 | 75 | let [ksize, vsize] = [(key.length).toString(16).padStart(16, '0'), (value.length).toString(16).padStart(16, '0')]; 76 | globalThis.callback._sendStorage(fr + ksize + vsize + key + value); 77 | } 78 | })(); 79 | `) 80 | } 81 | 82 | // AddLocalStorage implements the StorageManager interface. 83 | func (s *storageManager) AddLocalStorage(c StorageData) error { 84 | return s.JavascriptManager().RunJavaScript(` 85 | (function() { 86 | globalThis.localStorage.setItem("` + c.Key + `", "` + c.Value + `"); 87 | })(); 88 | `) 89 | } 90 | 91 | // RemoveLocalStorage implements the StorageManager interface. 92 | func (s *storageManager) RemoveLocalStorage(c StorageData) error { 93 | return s.JavascriptManager().RunJavaScript(` 94 | (function() { 95 | globalThis.localStorage.removeItem("` + c.Key + `"); 96 | })(); 97 | `) 98 | } 99 | 100 | // SessionStorage implements the StorageManager interface. 101 | func (s *storageManager) SessionStorage(fn DataLooper[StorageData]) (err error) { 102 | fr := internal.NewHandle(fn) 103 | defer fr.Delete() 104 | 105 | return s.JavascriptManager().RunJavaScript(` 106 | (function() { 107 | let fr = "` + strconv.FormatUint(uint64(fr), 16) + `".padStart(16, '0'); 108 | for (i = 0; i < globalThis.sessionStorage.length; i++) { 109 | let key = globalThis.sessionStorage.key(i); 110 | let value = globalThis.sessionStorage.getItem(key); 111 | 112 | let [ksize, vsize] = [(key.length).toString(16).padStart(16, '0'), (value.length).toString(16).padStart(16, '0')]; 113 | globalThis.callback._sendStorage(fr + ksize + vsize + key + value); 114 | } 115 | })(); 116 | `) 117 | } 118 | 119 | // AddSessionStorage implements the StorageManager interface. 120 | func (s *storageManager) AddSessionStorage(c StorageData) error { 121 | return s.JavascriptManager().RunJavaScript(` 122 | (function() { 123 | globalThis.sessionStorage.setItem("` + c.Key + `", "` + c.Value + `"); 124 | })(); 125 | `) 126 | } 127 | 128 | // RemoveSessionStorage implements the StorageManager interface. 129 | func (s *storageManager) RemoveSessionStorage(c StorageData) error { 130 | return s.JavascriptManager().RunJavaScript(` 131 | (function() { 132 | globalThis.sessionStorage.removeItem("` + c.Key + `"); 133 | })(); 134 | `) 135 | } 136 | -------------------------------------------------------------------------------- /webview/storage_android.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | /* 4 | #cgo CFLAGS: -Werror 5 | #cgo LDFLAGS: -landroid 6 | 7 | #include 8 | #include 9 | */ 10 | import "C" 11 | import ( 12 | "strconv" 13 | "time" 14 | "unsafe" 15 | 16 | "git.wow.st/gmp/jni" 17 | "github.com/inkeliz/giowebview/webview/internal" 18 | ) 19 | 20 | type cookieManager struct { 21 | *webview 22 | } 23 | 24 | func newCookieManager(w *webview) *cookieManager { 25 | r := &cookieManager{webview: w} 26 | return r 27 | } 28 | 29 | // Cookies implements the CookieManager interface. 30 | func (s *cookieManager) Cookies(fn DataLooper[CookieData]) (err error) { 31 | done := make(chan error) 32 | dr, fr := internal.NewHandle(done), internal.NewHandle(fn) 33 | defer dr.Delete() 34 | defer fr.Delete() 35 | 36 | s.scheduler.MustRun(func() { 37 | s.driver.callArgs("webview_getCookies", "(JJ)V", func(env jni.Env) []jni.Value { 38 | return []jni.Value{ 39 | jni.Value(int64(fr)), 40 | jni.Value(int64(dr)), 41 | } 42 | }) 43 | }) 44 | 45 | return <-done 46 | } 47 | 48 | //export Java_com_inkeliz_webview_sys_1android_getCookiesCallback 49 | func Java_com_inkeliz_webview_sys_1android_getCookiesCallback(env *C.JNIEnv, class C.jclass, ptr C.jlong, msg C.jstring) { 50 | raw := jni.GoString(jni.EnvFor(uintptr(unsafe.Pointer(env))), jni.String(msg)) 51 | c := CookieData{} 52 | 53 | last := len(raw) - 1 54 | start := 0 55 | ignore := false 56 | 57 | for i := 0; i < len(raw); i++ { 58 | if !ignore && raw[i] == '=' { 59 | c.Name = raw[start:i] 60 | start = i + 1 61 | ignore = true 62 | } 63 | if raw[i] == ';' || last == i { 64 | if last == i { 65 | i++ 66 | } 67 | c.Value = raw[start:i] 68 | start = i + 2 69 | ignore = false 70 | 71 | if !internal.Handle(ptr).Value().(DataLooper[CookieData])(&c) { 72 | break 73 | } 74 | 75 | c.Value, c.Name = "", "" 76 | } 77 | } 78 | } 79 | 80 | // AddCookie implements the CookieManager interface. 81 | func (s *cookieManager) AddCookie(c CookieData) error { 82 | done := make(chan error) 83 | dr := internal.NewHandle(done) 84 | defer dr.Delete() 85 | 86 | cookie := c.Name + "=" + c.Value + ";" 87 | if c.Domain != "" { 88 | cookie += " Domain=" + c.Domain + ";" 89 | } 90 | if c.Path != "" { 91 | cookie += " Path=" + c.Path + ";" 92 | } 93 | if c.Expires.After(time.Now()) { 94 | cookie += " Max-Age=" + strconv.FormatInt(int64(c.Expires.Sub(time.Now())/time.Second), 10) + ";" 95 | } 96 | if c.Features.IsSecure() { 97 | cookie += " Secure;" 98 | } 99 | if c.Features.IsHTTPOnly() { 100 | cookie += " HttpOnly;" 101 | } 102 | 103 | s.scheduler.MustRun(func() { 104 | s.driver.callArgs("webview_addCookie", "(Ljava/lang/String;Ljava/lang/String;J)V", func(env jni.Env) []jni.Value { 105 | return []jni.Value{ 106 | jni.Value(jni.JavaString(env, c.Domain)), 107 | jni.Value(jni.JavaString(env, cookie)), 108 | jni.Value(int64(dr)), 109 | } 110 | }) 111 | }) 112 | return <-done 113 | } 114 | 115 | // RemoveCookie implements the CookieManager interface. 116 | func (s *cookieManager) RemoveCookie(c CookieData) error { 117 | return ErrNotSupported 118 | } 119 | -------------------------------------------------------------------------------- /webview/storage_darwin.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | /* 4 | #cgo CFLAGS: -xobjective-c -fmodules -fobjc-arc 5 | 6 | #include 7 | #import 8 | 9 | extern void getCookies(CFTypeRef config, uintptr_t handler, uintptr_t done); 10 | extern void addCookie(CFTypeRef config, uintptr_t done, char *name, char *value, char *domain, char *path, int64_t expires, uint64_t features); 11 | extern void removeCookie(CFTypeRef config, uintptr_t done, char *name, char *domain, char *path); 12 | 13 | */ 14 | import "C" 15 | import ( 16 | "time" 17 | "unsafe" 18 | 19 | "github.com/inkeliz/giowebview/webview/internal" 20 | ) 21 | 22 | type cookieManager struct { 23 | *webview 24 | } 25 | 26 | func newCookieManager(w *webview) *cookieManager { 27 | r := &cookieManager{webview: w} 28 | return r 29 | } 30 | 31 | // Cookies implements the CookieManager interface. 32 | func (s *cookieManager) Cookies(fn DataLooper[CookieData]) (err error) { 33 | done := make(chan error) 34 | 35 | fr, dr := internal.NewHandle(fn), internal.NewHandle(done) 36 | defer fr.Delete() 37 | defer dr.Delete() 38 | 39 | s.scheduler.MustRun(func() { 40 | C.getCookies(s.driver.webviewConfig, C.uintptr_t(fr), C.uintptr_t(dr)) 41 | }) 42 | 43 | return <-done 44 | } 45 | 46 | //export getCookiesCallback 47 | func getCookiesCallback(handler uintptr, features uint64, name, value, domain, path *C.char, expires int64) bool { 48 | return internal.Handle(handler).Value().(DataLooper[CookieData])(&CookieData{ 49 | Name: C.GoString(name), 50 | Value: C.GoString(value), 51 | Domain: C.GoString(domain), 52 | Path: C.GoString(path), 53 | Expires: time.Unix(expires, 0), 54 | Features: CookieFeatures(features), 55 | }) 56 | } 57 | 58 | // AddCookie implements the CookieManager interface. 59 | func (s *cookieManager) AddCookie(c CookieData) error { 60 | done := make(chan error) 61 | dr := internal.NewHandle(done) 62 | defer dr.Delete() 63 | 64 | name, value, domain, path := C.CString(c.Name), C.CString(c.Value), C.CString(c.Domain), C.CString(c.Path) 65 | var expires C.int64_t 66 | if c.Expires.After(time.Now()) { 67 | expires = C.int64_t(c.Expires.Unix()) 68 | } 69 | 70 | defer C.free(unsafe.Pointer(name)) 71 | defer C.free(unsafe.Pointer(value)) 72 | defer C.free(unsafe.Pointer(domain)) 73 | defer C.free(unsafe.Pointer(path)) 74 | 75 | s.webview.scheduler.MustRun(func() { 76 | C.addCookie(s.driver.webviewConfig, C.uintptr_t(dr), name, value, domain, path, expires, C.uint64_t(c.Features)) 77 | }) 78 | return <-done 79 | } 80 | 81 | // RemoveCookie implements the CookieManager interface. 82 | func (s *cookieManager) RemoveCookie(c CookieData) error { 83 | done := make(chan error) 84 | dr := internal.NewHandle(done) 85 | defer dr.Delete() 86 | 87 | name := C.CString(c.Name) 88 | defer C.free(unsafe.Pointer(name)) 89 | domain := C.CString(c.Domain) 90 | defer C.free(unsafe.Pointer(domain)) 91 | path := C.CString(c.Path) 92 | defer C.free(unsafe.Pointer(path)) 93 | 94 | s.webview.scheduler.MustRun(func() { 95 | C.removeCookie(s.driver.webviewConfig, C.uintptr_t(dr), name, domain, path) 96 | }) 97 | 98 | return <-done 99 | } 100 | -------------------------------------------------------------------------------- /webview/storage_js.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | type cookieManager struct { 4 | *webview 5 | } 6 | 7 | func newCookieManager(w *webview) *cookieManager { 8 | return new(cookieManager) 9 | } 10 | 11 | // Cookies implements the CookieManager interface. 12 | func (*cookieManager) Cookies(_ DataLooper[CookieData]) (err error) { 13 | return ErrNotSupported 14 | } 15 | 16 | // AddCookie implements the CookieManager interface. 17 | func (*cookieManager) AddCookie(_ CookieData) error { 18 | return ErrNotSupported 19 | } 20 | 21 | // RemoveCookie implements the CookieManager interface. 22 | func (*cookieManager) RemoveCookie(_ CookieData) error { 23 | return ErrNotSupported 24 | } 25 | -------------------------------------------------------------------------------- /webview/storage_windows.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "math" 5 | "syscall" 6 | "time" 7 | "unsafe" 8 | 9 | "github.com/inkeliz/giowebview/webview/internal" 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | type cookieManager struct { 14 | *webview 15 | *_ICoreWebView2CookieManager 16 | } 17 | 18 | func newCookieManager(w *webview) *cookieManager { 19 | r := &cookieManager{webview: w} 20 | w.scheduler.MustRun(func() { 21 | syscall.SyscallN( 22 | r.webview.driver.webview22.VTBL.CookieManager, 23 | uintptr(unsafe.Pointer(r.webview.driver.webview22)), 24 | uintptr(unsafe.Pointer(&r._ICoreWebView2CookieManager)), 25 | ) 26 | }) 27 | return r 28 | } 29 | 30 | func (s *cookieManager) Cookies(fn DataLooper[CookieData]) (err error) { 31 | done := make(chan error) 32 | dr := internal.NewHandle(done) 33 | defer dr.Delete() 34 | 35 | handler := &_ICoreWebView2GetCookiesCompletedHandler{ 36 | VTBL: _CoreWebView2GetCookiesCompletedHandlerVTBL, 37 | Invoke: func(this *_ICoreWebView2GetCookiesCompletedHandler, err uintptr, cookies *_ICoreWebView2CookieList) uintptr { 38 | var ( 39 | cookie *_ICoreWebView2Cookie 40 | data CookieData 41 | length uint32 42 | ) 43 | 44 | syscall.SyscallN(cookies.VTBL.Count, uintptr(unsafe.Pointer(cookies)), uintptr(unsafe.Pointer(&length))) 45 | 46 | for index := uint32(0); index < length; index++ { 47 | syscall.SyscallN( 48 | cookies.VTBL.GetValueAtIndex, 49 | uintptr(unsafe.Pointer(cookies)), 50 | uintptr(index), 51 | uintptr(unsafe.Pointer(&cookie)), 52 | ) 53 | 54 | var name, value, domain, path *uint16 55 | syscall.SyscallN(cookie.VTBL.GetName, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&name))) 56 | syscall.SyscallN(cookie.VTBL.GetValue, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&value))) 57 | syscall.SyscallN(cookie.VTBL.GetDomain, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&domain))) 58 | syscall.SyscallN(cookie.VTBL.GetPath, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&path))) 59 | 60 | var expires float64 61 | syscall.SyscallN(cookie.VTBL.GetExpires, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&expires))) 62 | 63 | var secure, httponly uintptr 64 | syscall.SyscallN(cookie.VTBL.IsSecure, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&secure))) 65 | syscall.SyscallN(cookie.VTBL.IsHttpOnly, uintptr(unsafe.Pointer(cookie)), uintptr(unsafe.Pointer(&httponly))) 66 | 67 | data.Name = windows.UTF16PtrToString(name) 68 | data.Value = windows.UTF16PtrToString(value) 69 | data.Domain = windows.UTF16PtrToString(domain) 70 | data.Path = windows.UTF16PtrToString(path) 71 | data.Expires = time.Unix(int64(expires), 0) 72 | data.Features = 0 73 | if secure != 0 { 74 | data.Features |= CookieSecure 75 | } 76 | if httponly != 0 { 77 | data.Features |= CookieHTTPOnly 78 | } 79 | 80 | next := fn(&data) 81 | for _, v := range []*uint16{name, value, domain, path} { 82 | windows.CoTaskMemFree(unsafe.Pointer(v)) 83 | } 84 | 85 | if !next { 86 | break 87 | } 88 | } 89 | 90 | done <- nil 91 | return 0 92 | }, 93 | } 94 | 95 | var url uintptr 96 | s.scheduler.MustRun(func() { 97 | syscall.SyscallN( 98 | s.webview.driver.webview2.VTBL.GetSource, 99 | uintptr(unsafe.Pointer(s.webview.driver.webview2)), 100 | uintptr(unsafe.Pointer(&url)), 101 | ) 102 | 103 | syscall.SyscallN( 104 | s._ICoreWebView2CookieManager.VTBL.GetCookies, 105 | uintptr(unsafe.Pointer(s._ICoreWebView2CookieManager)), 106 | url, 107 | uintptr(unsafe.Pointer(handler)), 108 | ) 109 | }) 110 | 111 | return <-done 112 | } 113 | 114 | func (s *cookieManager) AddCookie(c CookieData) error { 115 | s.scheduler.MustRun(func() { 116 | var cookie *_ICoreWebView2Cookie 117 | syscall.SyscallN( 118 | s._ICoreWebView2CookieManager.VTBL.CreateCookie, 119 | uintptr(unsafe.Pointer(s._ICoreWebView2CookieManager)), 120 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Name))), 121 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Value))), 122 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Domain))), 123 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Path))), 124 | uintptr(unsafe.Pointer(&cookie)), 125 | ) 126 | 127 | expiresFloat := float64(c.Expires.Unix()) 128 | syscall.SyscallN(cookie.VTBL.PutExpires, uintptr(unsafe.Pointer(cookie)), *(*uintptr)(unsafe.Pointer(&expiresFloat))) 129 | if c.Features.IsSecure() { 130 | syscall.SyscallN(cookie.VTBL.PutSecure, uintptr(unsafe.Pointer(cookie)), uintptr(math.MaxInt)) 131 | } 132 | if c.Features.IsHTTPOnly() { 133 | syscall.SyscallN(cookie.VTBL.PutHttpOnly, uintptr(unsafe.Pointer(cookie)), uintptr(math.MaxInt)) 134 | } 135 | 136 | syscall.SyscallN( 137 | s._ICoreWebView2CookieManager.VTBL.AddOrUpdateCookie, 138 | uintptr(unsafe.Pointer(s._ICoreWebView2CookieManager)), 139 | uintptr(unsafe.Pointer(cookie)), 140 | ) 141 | }) 142 | 143 | return nil 144 | } 145 | 146 | func (s *cookieManager) RemoveCookie(c CookieData) error { 147 | s.scheduler.MustRun(func() { 148 | syscall.SyscallN( 149 | s._ICoreWebView2CookieManager.VTBL.DeleteCookiesWithDomainAndPath, 150 | uintptr(unsafe.Pointer(s._ICoreWebView2CookieManager)), 151 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Name))), 152 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Domain))), 153 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(c.Path))), 154 | ) 155 | }) 156 | 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /webview/sys_android.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | /* 4 | #cgo CFLAGS: -Werror 5 | #cgo LDFLAGS: -landroid 6 | 7 | #include 8 | #include 9 | */ 10 | import "C" 11 | import ( 12 | "errors" 13 | "unsafe" 14 | 15 | "git.wow.st/gmp/jni" 16 | "github.com/inkeliz/giowebview/webview/internal" 17 | ) 18 | 19 | //export Java_com_inkeliz_webview_sys_1android_reportDone 20 | func Java_com_inkeliz_webview_sys_1android_reportDone(env *C.JNIEnv, class C.jclass, done C.jlong, v C.jstring) { 21 | if done == 0 { 22 | return 23 | } 24 | 25 | var r error 26 | if err := jni.GoString(jni.EnvFor(uintptr(unsafe.Pointer(env))), jni.String(v)); err != "" { 27 | r = errors.New(err) 28 | } 29 | 30 | go func() { 31 | internal.Handle(uintptr(done)).Value().(chan error) <- r 32 | }() 33 | } 34 | 35 | //export Java_com_inkeliz_webview_sys_1android_reportLoadStatus 36 | func Java_com_inkeliz_webview_sys_1android_reportLoadStatus(env *C.JNIEnv, class C.jclass, handler C.jlong, v C.jstring) { 37 | url := jni.GoString(jni.EnvFor(uintptr(unsafe.Pointer(env))), jni.String(v)) 38 | internal.Handle(uintptr(handler)).Value().(*webview).fan.Send(NavigationEvent{ 39 | URL: url, 40 | }) 41 | } 42 | 43 | //export Java_com_inkeliz_webview_sys_1android_reportTitleStatus 44 | func Java_com_inkeliz_webview_sys_1android_reportTitleStatus(env *C.JNIEnv, class C.jclass, handler C.jlong, v C.jstring) { 45 | title := jni.GoString(jni.EnvFor(uintptr(unsafe.Pointer(env))), jni.String(v)) 46 | internal.Handle(uintptr(handler)).Value().(*webview).fan.Send(TitleEvent{ 47 | Title: title, 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /webview/sys_android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkeliz/giowebview/dd13eba9780e94f9623fb4e1282f3462d4565de7/webview/sys_android.jar -------------------------------------------------------------------------------- /webview/sys_android.java: -------------------------------------------------------------------------------- 1 | package com.inkeliz.webview; 2 | 3 | import android.os.Bundle; 4 | import android.view.ViewGroup; 5 | import android.app.Activity; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.FrameLayout; 9 | import android.view.KeyEvent; 10 | import android.webkit.WebSettings; 11 | import android.content.Context; 12 | import android.webkit.WebViewClient; 13 | import android.widget.Toast; 14 | import android.webkit.WebView; 15 | import android.webkit.WebChromeClient; 16 | import android.util.Log; 17 | import android.os.Build; 18 | import android.os.Parcelable; 19 | import android.net.Proxy; 20 | import java.lang.reflect.*; 21 | import android.util.ArrayMap; 22 | import android.content.Intent; 23 | import java.util.concurrent.Semaphore; 24 | import android.net.http.SslError; 25 | import android.webkit.SslErrorHandler; 26 | import java.security.cert.Certificate; 27 | import android.net.http.SslCertificate; 28 | import java.security.PublicKey; 29 | import java.security.cert.X509Certificate; 30 | import java.security.MessageDigest; 31 | import java.security.cert.CertificateFactory; 32 | import java.io.ByteArrayInputStream; 33 | import java.io.InputStream; 34 | import java.util.ArrayList; 35 | import android.os.Build.VERSION; 36 | import android.os.Build.VERSION_CODES; 37 | import android.util.Base64; 38 | import android.webkit.URLUtil; 39 | import android.webkit.WebResourceRequest; 40 | import android.webkit.JavascriptInterface; 41 | import android.graphics.Bitmap; 42 | import android.webkit.ValueCallback; 43 | import android.webkit.CookieManager; 44 | import java.lang.Boolean; 45 | 46 | public class sys_android { 47 | private ViewGroup primaryView; 48 | private WebView webBrowser; 49 | private PublicKey[] additionalCerts; 50 | private ArrayList onLoadAdditionalScripts; 51 | private ArrayList onFinishAdditionalScripts; 52 | 53 | public class gowebview_javascript { 54 | public long handler; 55 | 56 | gowebview_javascript(long handler) { 57 | this.handler = handler; 58 | } 59 | 60 | @JavascriptInterface 61 | public String callback(String msg) { 62 | sendCallback(handler, msg); 63 | return ""; 64 | } 65 | } 66 | 67 | public class gowebview_webbrowser extends WebViewClient { 68 | public long handler; 69 | 70 | @Override 71 | public void onPageStarted(WebView v, String url, Bitmap favicon) { 72 | super.onPageStarted(v, url, favicon); 73 | if (onLoadAdditionalScripts != null) { 74 | for (int i = 0; i < onLoadAdditionalScripts.size(); i++) { 75 | v.evaluateJavascript(onLoadAdditionalScripts.get(i), null); 76 | } 77 | } 78 | reportLoadStatus(handler, url); 79 | } 80 | 81 | @Override 82 | public void onPageFinished(WebView v, String url) { 83 | super.onPageFinished(v, url); 84 | if (onFinishAdditionalScripts != null) { 85 | for (int i = 0; i < onFinishAdditionalScripts.size(); i++) { 86 | v.evaluateJavascript(onFinishAdditionalScripts.get(i), null); 87 | } 88 | } 89 | } 90 | 91 | @Override public boolean shouldOverrideUrlLoading(WebView v, WebResourceRequest request) { 92 | String url = request.getUrl().toString(); 93 | if (url.isEmpty()) { 94 | return false; 95 | } 96 | if (URLUtil.isNetworkUrl(url)) { 97 | return false; 98 | } 99 | return true; 100 | } 101 | 102 | @Override public void onReceivedSslError(WebView v, final SslErrorHandler sslHandler, SslError err){ 103 | if (additionalCerts == null || additionalCerts.length == 0) { 104 | super.onReceivedSslError(v, sslHandler, err); 105 | return; 106 | } 107 | 108 | Certificate certificate = null; 109 | try{ 110 | if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.Q) { 111 | certificate = err.getCertificate().getX509Certificate(); 112 | } else { 113 | // Old APIs doesn't have such .getX509Certificate() 114 | Bundle bundle = SslCertificate.saveState(err.getCertificate()); 115 | byte[] certificateBytes = bundle.getByteArray("x509-certificate"); 116 | if (certificateBytes != null) { 117 | CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 118 | certificate = certFactory.generateCertificate(new ByteArrayInputStream(certificateBytes)); 119 | } 120 | } 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | } 124 | 125 | if (certificate == null) { 126 | super.onReceivedSslError(v, sslHandler, err); 127 | return; 128 | } 129 | 130 | for (int i = 0; i < additionalCerts.length; i++) { 131 | try{ 132 | certificate.verify(additionalCerts[i]); 133 | sslHandler.proceed(); 134 | return; 135 | } catch (Exception e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | 140 | super.onReceivedSslError(v, sslHandler, err); 141 | } 142 | } 143 | 144 | public class gowebview_chrome extends WebChromeClient { 145 | public long handler; 146 | 147 | @Override 148 | public void onReceivedTitle(WebView view, String title) { 149 | super.onReceivedTitle(view, title); 150 | reportTitleStatus(handler, title); 151 | } 152 | } 153 | 154 | public void webview_set_callback(long handler) { 155 | webBrowser.addJavascriptInterface(new gowebview_javascript(handler), "_callback"); 156 | } 157 | 158 | public void webview_install_javascript(String js, long when, long done) { 159 | if (when == 0) { 160 | onLoadAdditionalScripts.add(js); 161 | } else { 162 | onFinishAdditionalScripts.add(js); 163 | } 164 | reportDone(done, ""); 165 | } 166 | 167 | public void webview_run_javascript(String js, long done) { 168 | webBrowser.evaluateJavascript(js, new ValueCallback() { 169 | @Override 170 | public void onReceiveValue(String value) { 171 | reportDone(done, ""); 172 | } 173 | }); 174 | } 175 | 176 | public void webview_create(View v, long handler) { 177 | if (primaryView == null) { 178 | if (v instanceof ViewGroup) { 179 | primaryView = (ViewGroup) v; 180 | } else { 181 | primaryView = (ViewGroup) v.getParent(); 182 | } 183 | } 184 | 185 | onLoadAdditionalScripts = new ArrayList(); 186 | onFinishAdditionalScripts = new ArrayList(); 187 | 188 | webBrowser = new WebView(v.getContext()); 189 | WebSettings webSettings = webBrowser.getSettings(); 190 | webSettings.setJavaScriptEnabled(true); 191 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) { 192 | webSettings.setSafeBrowsingEnabled(false); 193 | } 194 | webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 195 | webSettings.setUseWideViewPort(true); 196 | webSettings.setLoadWithOverviewMode(true); 197 | webSettings.setDomStorageEnabled(true); 198 | webSettings.setDatabaseEnabled(true); 199 | 200 | webBrowser.setBackgroundColor(0xFFFFFFFF); 201 | 202 | gowebview_webbrowser wb = new gowebview_webbrowser(); 203 | wb.handler = handler; 204 | webBrowser.setWebViewClient(wb); 205 | 206 | gowebview_chrome wc = new gowebview_chrome(); 207 | wc.handler = handler; 208 | webBrowser.setWebChromeClient(wc); 209 | 210 | webBrowser.setVisibility(View.VISIBLE); 211 | 212 | primaryView.addView(webBrowser); 213 | primaryView.bringChildToFront(webBrowser); 214 | } 215 | 216 | public void webview_resize(int x, int y, int width, int height) { 217 | webBrowser.setLayoutParams(new FrameLayout.LayoutParams(width, height)); 218 | webBrowser.setX(x); 219 | webBrowser.setY(y); 220 | } 221 | 222 | public void webview_getCookies(long handler, long done) { 223 | CookieManager cookieManager = CookieManager.getInstance(); 224 | String cookie = cookieManager.getCookie(webBrowser.getUrl()); 225 | 226 | getCookiesCallback(handler, cookie); 227 | reportDone(done, ""); 228 | } 229 | 230 | public void webview_addCookie(String domain, String cookie, long done) { 231 | CookieManager cookieManager = CookieManager.getInstance(); 232 | cookieManager.setCookie(domain, cookie, new ValueCallback() { 233 | @Override 234 | public void onReceiveValue(Boolean value) { 235 | cookieManager.flush(); 236 | reportDone(done, ""); 237 | } 238 | }); 239 | } 240 | 241 | public void webview_navigate(String url) { 242 | webBrowser.loadUrl(url); 243 | } 244 | 245 | public void webview_destroy() { 246 | webBrowser.onPause(); 247 | webBrowser.removeAllViews(); 248 | webBrowser.pauseTimers(); 249 | webBrowser.destroy(); 250 | } 251 | 252 | public void webview_show() { 253 | webBrowser.setVisibility(View.VISIBLE); 254 | } 255 | 256 | public void webview_hide() { 257 | webBrowser.setVisibility(View.GONE); 258 | } 259 | 260 | public boolean webview_proxy(String host, String port) { 261 | final Semaphore mutex = new Semaphore(0); 262 | 263 | Context app = webBrowser.getContext().getApplicationContext(); 264 | 265 | System.setProperty("http.proxyHost", host); 266 | System.setProperty("http.proxyPort", port); 267 | System.setProperty("https.proxyHost", host); 268 | System.setProperty("https.proxyPort", port); 269 | 270 | try { 271 | Field apk = app.getClass().getDeclaredField("mLoadedApk"); 272 | apk.setAccessible(true); 273 | 274 | Field receivers = Class.forName("android.app.LoadedApk").getDeclaredField("mReceivers"); 275 | receivers.setAccessible(true); 276 | 277 | for (Object map : ((ArrayMap) receivers.get(apk.get(app))).values()) { 278 | 279 | for (Object receiver : ((ArrayMap) map).keySet()) { 280 | 281 | Class cls = receiver.getClass(); 282 | if (cls.getName().contains("ProxyChangeListener")) { 283 | 284 | String proxyInfoName = "android.net.ProxyInfo"; 285 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 286 | proxyInfoName = "android.net.ProxyProperties"; 287 | } 288 | 289 | Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); 290 | 291 | Class proxyInfoClass = Class.forName(proxyInfoName); 292 | if (proxyInfoClass != null) { 293 | Constructor proxyInfo = proxyInfoClass.getConstructor(String.class, Integer.TYPE, String.class); 294 | proxyInfo.setAccessible(true); 295 | intent.putExtra("proxy", (Parcelable) ((Object) proxyInfo.newInstance(host, Integer.parseInt(port), null))); 296 | } 297 | 298 | cls.getDeclaredMethod("onReceive", Context.class, Intent.class).invoke(receiver, app, intent); 299 | } 300 | } 301 | 302 | } 303 | 304 | return true; 305 | } catch(Exception e) { 306 | return false; 307 | } 308 | } 309 | 310 | public boolean webview_certs(String der) { 311 | String[] sCerts = der.split(";"); 312 | 313 | additionalCerts = new PublicKey[sCerts.length]; 314 | 315 | for (int i = 0; i < sCerts.length; i++) { 316 | InputStream streamCert = new ByteArrayInputStream(Base64.decode(sCerts[i], android.util.Base64.DEFAULT)); 317 | 318 | try { 319 | CertificateFactory factory = CertificateFactory.getInstance("X.509"); 320 | X509Certificate cert = (X509Certificate)factory.generateCertificate(streamCert); 321 | 322 | additionalCerts[i] = cert.getPublicKey(); 323 | } catch(Exception e) { 324 | e.printStackTrace(); 325 | return false; 326 | } 327 | } 328 | 329 | return true; 330 | } 331 | 332 | 333 | static private native void reportDone(long handler, String error); 334 | static private native void sendCallback(long handler, String msg); 335 | static private native void getCookiesCallback(long handler, String cookies); 336 | static private native void reportTitleStatus(long handler, String title); 337 | static private native void reportLoadStatus(long handler, String status); 338 | } -------------------------------------------------------------------------------- /webview/sys_darwin.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import "C" 4 | import ( 5 | "errors" 6 | "github.com/inkeliz/giowebview/webview/internal" 7 | ) 8 | 9 | //export reportDone 10 | func reportDone(done uintptr, e *C.char) { 11 | if err := C.GoString(e); err != "" { 12 | internal.Handle(done).Value().(chan error) <- errors.New(err) 13 | } else { 14 | internal.Handle(done).Value().(chan error) <- nil 15 | } 16 | } 17 | 18 | //export reportLoadStatus 19 | func reportLoadStatus(handler uintptr, url *C.char) { 20 | internal.Handle(handler).Value().(*webview).fan.Send(NavigationEvent{ 21 | URL: C.GoString(url), 22 | }) 23 | } 24 | 25 | //export reportTitleStatus 26 | func reportTitleStatus(handler uintptr, title *C.char) { 27 | internal.Handle(handler).Value().(*webview).fan.Send(TitleEvent{ 28 | Title: C.GoString(title), 29 | }) 30 | } 31 | 32 | /* 33 | func _reportDone(msg string) { 34 | if len(msg) < 16 { 35 | return 36 | } 37 | ptr, err := strconv.ParseUint(msg, 16, 64) 38 | if err != nil { 39 | return 40 | } 41 | reportDone(uintptr(ptr), nil) 42 | } 43 | */ 44 | -------------------------------------------------------------------------------- /webview/sys_darwin.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include <_cgo_export.h> 3 | #include 4 | 5 | #if TARGET_OS_IPHONE 6 | @import UIKit; 7 | #else 8 | @import AppKit; 9 | #endif 10 | 11 | @import WebKit; 12 | 13 | @interface callbackHandler : NSObject 14 | @property (nonatomic, assign) uintptr_t handler; 15 | @end 16 | 17 | @implementation callbackHandler 18 | uintptr_t handler; 19 | 20 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { 21 | javascriptManagerCallback(_handler, (char *)[[message body] UTF8String]); 22 | } 23 | @end 24 | 25 | @interface giowebview : WKWebView 26 | @property (nonatomic, assign) uintptr_t handler; 27 | @end 28 | 29 | @implementation giowebview 30 | uintptr_t handler; 31 | 32 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 33 | if ([keyPath isEqual:@("URL")]) { 34 | reportLoadStatus(self.handler, (char *)[[[self URL] absoluteString] UTF8String]); 35 | return; 36 | } 37 | if ([keyPath isEqual:@("title")]) { 38 | reportTitleStatus(self.handler, (char *)[[self title] UTF8String]); 39 | return; 40 | } 41 | } 42 | @end 43 | 44 | CFTypeRef config() { 45 | WKWebViewConfiguration *conf = [[WKWebViewConfiguration alloc] init]; 46 | return CFBridgingRetain(conf); 47 | } 48 | 49 | CFTypeRef create(CFTypeRef config, uintptr_t handler) { 50 | giowebview *webView = [[giowebview alloc] initWithFrame:CGRectMake(0,0,0,0) configuration: (__bridge WKWebViewConfiguration *)config]; 51 | webView.handler = handler; 52 | 53 | NSString * watch[2] = { @("URL"), @("title") }; 54 | for (uint64_t i = 0; i < 2; i++) { 55 | [webView addObserver:webView forKeyPath:watch[i] options:NSKeyValueObservingOptionNew context:NULL]; 56 | } 57 | 58 | #if TARGET_OS_IPHONE 59 | 60 | #else 61 | NSColor * c = [NSColor clearColor]; 62 | webView.layer.backgroundColor = [c CGColor]; 63 | webView.layer.opaque = false; 64 | #endif 65 | 66 | return CFBridgingRetain(webView); 67 | } 68 | 69 | void resize(CFTypeRef web, CFTypeRef windowRef, float x, float y, float w, float h) { 70 | WKWebView *webView = (__bridge WKWebView *)web; 71 | #if TARGET_OS_IPHONE 72 | UIView *view = (__bridge UIView *)windowRef; 73 | #else 74 | NSView *view = (__bridge NSView *)windowRef; 75 | y = (view.bounds.size.height - h) - y; 76 | #endif 77 | 78 | [webView setFrame: CGRectMake((CGFloat)x, (CGFloat)y, (CGFloat)w, (CGFloat)h)]; 79 | } 80 | 81 | void show(CFTypeRef web) { 82 | WKWebView *webView = (__bridge WKWebView *)web; 83 | [webView setHidden:NO]; 84 | } 85 | 86 | void hide(CFTypeRef web) { 87 | WKWebView *webView = (__bridge WKWebView *)web; 88 | [webView setHidden:YES]; 89 | } 90 | 91 | void seturl(CFTypeRef web, char *u) { 92 | WKWebView *webView = (__bridge WKWebView *)web; 93 | 94 | NSURL *url = [NSURL URLWithString:@(u)]; 95 | NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; 96 | [webView loadRequest:requestObj]; 97 | } 98 | 99 | void run(CFTypeRef web, CFTypeRef windowRef) { 100 | WKWebView *webView = (__bridge WKWebView *)web; 101 | #if TARGET_OS_IPHONE 102 | UIView *view = [((__bridge UIViewController *)windowRef) view]; 103 | #else 104 | NSView *view = (__bridge NSView *)windowRef; 105 | #endif 106 | 107 | [webView setBounds: view.frame]; 108 | [view addSubview:webView]; 109 | } 110 | 111 | void getCookies(CFTypeRef config, uintptr_t handler, uintptr_t done) { 112 | NSISO8601DateFormatter *dateFormatting = [[NSISO8601DateFormatter alloc] init]; 113 | WKWebViewConfiguration *configuration = (__bridge WKWebViewConfiguration *)config; 114 | [[[configuration websiteDataStore] httpCookieStore] getAllCookies: ^(NSArray * array) { 115 | int i = 0; 116 | while(i < [array count]) { 117 | NSHTTPCookie *cookie = [array objectAtIndex:i]; 118 | 119 | int cookieType = 0; 120 | if([cookie isHTTPOnly]) { 121 | cookieType |= 1; 122 | } 123 | if([cookie isSecure]) { 124 | cookieType |= 2; 125 | } 126 | 127 | bool next = getCookiesCallback( 128 | handler, 129 | cookieType, 130 | (char *)[[cookie name] UTF8String], 131 | (char *)[[cookie value] UTF8String], 132 | (char *)[[cookie domain] UTF8String], 133 | (char *)[[cookie path] UTF8String], 134 | (int)[[cookie expiresDate] timeIntervalSince1970] 135 | ); 136 | 137 | if (!next) { 138 | break; 139 | } 140 | 141 | i++; 142 | } 143 | reportDone(done, nil); 144 | }]; 145 | } 146 | 147 | void addCookie(CFTypeRef config, uintptr_t done, char *name, char *value, char *domain, char *path, int64_t expires, uint64_t features) { 148 | WKWebViewConfiguration *configuration = (__bridge WKWebViewConfiguration *)config; 149 | NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ 150 | NSHTTPCookieName: @(name), 151 | NSHTTPCookieValue: @(value), 152 | NSHTTPCookieDomain: @(domain), 153 | NSHTTPCookiePath: @(path), 154 | NSHTTPCookieExpires: [NSDate dateWithTimeIntervalSince1970:expires], 155 | NSHTTPCookieSecure: ((features) == 2 ? @("true") : @("false")), 156 | }]; 157 | 158 | [[[configuration websiteDataStore] httpCookieStore] setCookie:cookie completionHandler: ^(void) { 159 | reportDone(done, nil); 160 | }]; 161 | } 162 | 163 | void removeCookie(CFTypeRef config, uintptr_t done, char *name, char *domain, char *path) { 164 | // Copy the information to prevent be freed by the GC/C.free. 165 | NSString *nameString = @(name); 166 | NSString *domainString = @(domain); 167 | NSString *pathString = @(path); 168 | 169 | WKWebViewConfiguration *configuration = (__bridge WKWebViewConfiguration *)config; 170 | [[[configuration websiteDataStore] httpCookieStore] getAllCookies: ^(NSArray * array) { 171 | int i = 0; 172 | while(i < [array count]) { 173 | NSHTTPCookie *cookie = [array objectAtIndex:i]; 174 | 175 | if ([[cookie name] isEqualToString:nameString] && [[cookie domain] isEqualToString:domainString] && [[cookie path] isEqualToString:pathString]) { 176 | [[[configuration websiteDataStore] httpCookieStore] deleteCookie:cookie completionHandler: ^(void) { 177 | reportDone(done, nil); 178 | }]; 179 | 180 | return; 181 | } 182 | 183 | i++; 184 | } 185 | reportDone(done, nil); 186 | }]; 187 | } 188 | 189 | void installJavascript(CFTypeRef config, char *js, uint64_t when) { 190 | WKWebViewConfiguration *configuration = (__bridge WKWebViewConfiguration *)config; 191 | WKUserContentController *controller = [configuration userContentController]; 192 | 193 | WKUserScriptInjectionTime time = WKUserScriptInjectionTimeAtDocumentStart; 194 | if (when == 1) { 195 | time = WKUserScriptInjectionTimeAtDocumentEnd; 196 | } 197 | 198 | [controller addUserScript:[[WKUserScript alloc] initWithSource:@(js) injectionTime:time forMainFrameOnly:false]]; 199 | } 200 | 201 | void runJavascript(CFTypeRef web, char *js, uintptr_t done) { 202 | WKWebView *webView = (__bridge WKWebView *)web; 203 | [webView evaluateJavaScript:@(js) completionHandler: ^(id result, NSError *error) { 204 | reportDone(done, (char *)[[error localizedDescription] UTF8String]); 205 | }]; 206 | } 207 | 208 | void addCallbackJavascript(CFTypeRef config, char *name, uintptr_t handler) { 209 | WKWebViewConfiguration *configuration = (__bridge WKWebViewConfiguration *)config; 210 | WKUserContentController *controller = [configuration userContentController]; 211 | 212 | callbackHandler *scriptMessageHandler = [callbackHandler alloc]; 213 | scriptMessageHandler.handler = handler; 214 | 215 | [controller addScriptMessageHandler:scriptMessageHandler name:@(name)]; 216 | } 217 | -------------------------------------------------------------------------------- /webview/sys_js.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "github.com/inkeliz/go_inkwasm/inkwasm" 5 | ) 6 | 7 | //inkwasm:func console.log 8 | func log(s inkwasm.Object) 9 | 10 | //inkwasm:func .querySelector 11 | func querySelector(o inkwasm.Object, s string) inkwasm.Object 12 | 13 | //inkwasm:func document.createElement 14 | func createElement(s string) inkwasm.Object 15 | 16 | //inkwasm:func .setAttribute 17 | func setAttribute(o inkwasm.Object, s string, v string) 18 | 19 | //inkwasm:set .innerText 20 | func setInnerText(o inkwasm.Object, s string) 21 | 22 | //inkwasm:func .prepend 23 | func prepend(o inkwasm.Object, c inkwasm.Object) 24 | 25 | //inkwasm:func .removeChild 26 | func removeChild(o inkwasm.Object, c inkwasm.Object) 27 | 28 | //inkwasm:set .style.width 29 | func setStyleWidth(o inkwasm.Object, s string) 30 | 31 | //inkwasm:set .style.height 32 | func setStyleHeight(o inkwasm.Object, s string) 33 | 34 | //inkwasm:set .style.position 35 | func setStylePosition(o inkwasm.Object, s string) 36 | 37 | //inkwasm:set .style.zIndex 38 | func setStyleZIndex(o inkwasm.Object, s string) 39 | 40 | //inkwasm:set .style.border 41 | func setStyleBorder(o inkwasm.Object, s string) 42 | 43 | //inkwasm:set .style.display 44 | func setStyleDisplay(o inkwasm.Object, s string) 45 | 46 | //inkwasm:set .style.top 47 | func setStyleTop(o inkwasm.Object, s string) 48 | 49 | //inkwasm:set .style.left 50 | func setStyleLeft(o inkwasm.Object, s string) 51 | 52 | //inkwasm:set .src 53 | func setSrc(o inkwasm.Object, s string) 54 | -------------------------------------------------------------------------------- /webview/sys_js.js: -------------------------------------------------------------------------------- 1 | // Code generated by INKWASM BUILD; DO NOT EDIT 2 | (() => { 3 | 4 | })(); 5 | // Code generated by INKWASM BUILD; DO NOT EDIT 6 | (() => { 7 | Object.assign(go.importObject.go, { 8 | 9 | "github.com/inkeliz/giowebview/webview.__log": (sp) => { 10 | console.log(globalThis.inkwasm.Load.InkwasmObject(go, sp, 8)) 11 | 12 | }, 13 | 14 | "github.com/inkeliz/giowebview/webview.__querySelector": (sp) => { 15 | let r = globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).querySelector(globalThis.inkwasm.Load.String(go, sp, 24)) 16 | sp = go._inst.exports.getsp() >>> 0 17 | globalThis.inkwasm.Set.InkwasmObject(go, sp, 40, r) 18 | }, 19 | 20 | "github.com/inkeliz/giowebview/webview.__createElement": (sp) => { 21 | let r = document.createElement(globalThis.inkwasm.Load.String(go, sp, 8)) 22 | sp = go._inst.exports.getsp() >>> 0 23 | globalThis.inkwasm.Set.InkwasmObject(go, sp, 24, r) 24 | }, 25 | 26 | "github.com/inkeliz/giowebview/webview.__setAttribute": (sp) => { 27 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).setAttribute(globalThis.inkwasm.Load.String(go, sp, 24),globalThis.inkwasm.Load.String(go, sp, 40)) 28 | 29 | }, 30 | 31 | "github.com/inkeliz/giowebview/webview.__setInnerText": (sp) => { 32 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).innerText = globalThis.inkwasm.Load.String(go, sp, 24) 33 | 34 | }, 35 | 36 | "github.com/inkeliz/giowebview/webview.__prepend": (sp) => { 37 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).prepend(globalThis.inkwasm.Load.InkwasmObject(go, sp, 24)) 38 | 39 | }, 40 | 41 | "github.com/inkeliz/giowebview/webview.__removeChild": (sp) => { 42 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).removeChild(globalThis.inkwasm.Load.InkwasmObject(go, sp, 24)) 43 | 44 | }, 45 | 46 | "github.com/inkeliz/giowebview/webview.__setStyleWidth": (sp) => { 47 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.width = globalThis.inkwasm.Load.String(go, sp, 24) 48 | 49 | }, 50 | 51 | "github.com/inkeliz/giowebview/webview.__setStyleHeight": (sp) => { 52 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.height = globalThis.inkwasm.Load.String(go, sp, 24) 53 | 54 | }, 55 | 56 | "github.com/inkeliz/giowebview/webview.__setStylePosition": (sp) => { 57 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.position = globalThis.inkwasm.Load.String(go, sp, 24) 58 | 59 | }, 60 | 61 | "github.com/inkeliz/giowebview/webview.__setStyleZIndex": (sp) => { 62 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.zIndex = globalThis.inkwasm.Load.String(go, sp, 24) 63 | 64 | }, 65 | 66 | "github.com/inkeliz/giowebview/webview.__setStyleBorder": (sp) => { 67 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.border = globalThis.inkwasm.Load.String(go, sp, 24) 68 | 69 | }, 70 | 71 | "github.com/inkeliz/giowebview/webview.__setStyleDisplay": (sp) => { 72 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.display = globalThis.inkwasm.Load.String(go, sp, 24) 73 | 74 | }, 75 | 76 | "github.com/inkeliz/giowebview/webview.__setStyleTop": (sp) => { 77 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.top = globalThis.inkwasm.Load.String(go, sp, 24) 78 | 79 | }, 80 | 81 | "github.com/inkeliz/giowebview/webview.__setStyleLeft": (sp) => { 82 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).style.left = globalThis.inkwasm.Load.String(go, sp, 24) 83 | 84 | }, 85 | 86 | "github.com/inkeliz/giowebview/webview.__setSrc": (sp) => { 87 | globalThis.inkwasm.Load.InkwasmObject(go, sp, 8).src = globalThis.inkwasm.Load.String(go, sp, 24) 88 | 89 | }, 90 | 91 | }) 92 | })(); 93 | -------------------------------------------------------------------------------- /webview/sys_js_wasm.go: -------------------------------------------------------------------------------- 1 | // Code generated by INKWASM BUILD; DO NOT EDIT 2 | 3 | package webview 4 | 5 | import ( 6 | "runtime" 7 | 8 | "github.com/inkeliz/go_inkwasm/inkwasm" 9 | ) 10 | 11 | func _log(s inkwasm.Object) { 12 | __log(s) 13 | 14 | } 15 | func __log(s inkwasm.Object) 16 | 17 | func _querySelector(o inkwasm.Object, s string) (_ inkwasm.Object) { 18 | r0 := __querySelector(o, s) 19 | runtime.KeepAlive(s) 20 | 21 | return r0 22 | } 23 | func __querySelector(o inkwasm.Object, s string) (_ inkwasm.Object) 24 | 25 | func _createElement(s string) (_ inkwasm.Object) { 26 | r0 := __createElement(s) 27 | runtime.KeepAlive(s) 28 | 29 | return r0 30 | } 31 | func __createElement(s string) (_ inkwasm.Object) 32 | 33 | func _setAttribute(o inkwasm.Object, s string, v string) { 34 | __setAttribute(o, s, v) 35 | runtime.KeepAlive(s) 36 | runtime.KeepAlive(v) 37 | 38 | } 39 | func __setAttribute(o inkwasm.Object, s string, v string) 40 | 41 | func _setInnerText(o inkwasm.Object, s string) { 42 | __setInnerText(o, s) 43 | runtime.KeepAlive(s) 44 | 45 | } 46 | func __setInnerText(o inkwasm.Object, s string) 47 | 48 | func _prepend(o inkwasm.Object, c inkwasm.Object) { 49 | __prepend(o, c) 50 | 51 | } 52 | func __prepend(o inkwasm.Object, c inkwasm.Object) 53 | 54 | func _removeChild(o inkwasm.Object, c inkwasm.Object) { 55 | __removeChild(o, c) 56 | 57 | } 58 | func __removeChild(o inkwasm.Object, c inkwasm.Object) 59 | 60 | func _setStyleWidth(o inkwasm.Object, s string) { 61 | __setStyleWidth(o, s) 62 | runtime.KeepAlive(s) 63 | 64 | } 65 | func __setStyleWidth(o inkwasm.Object, s string) 66 | 67 | func _setStyleHeight(o inkwasm.Object, s string) { 68 | __setStyleHeight(o, s) 69 | runtime.KeepAlive(s) 70 | 71 | } 72 | func __setStyleHeight(o inkwasm.Object, s string) 73 | 74 | func _setStylePosition(o inkwasm.Object, s string) { 75 | __setStylePosition(o, s) 76 | runtime.KeepAlive(s) 77 | 78 | } 79 | func __setStylePosition(o inkwasm.Object, s string) 80 | 81 | func _setStyleZIndex(o inkwasm.Object, s string) { 82 | __setStyleZIndex(o, s) 83 | runtime.KeepAlive(s) 84 | 85 | } 86 | func __setStyleZIndex(o inkwasm.Object, s string) 87 | 88 | func _setStyleBorder(o inkwasm.Object, s string) { 89 | __setStyleBorder(o, s) 90 | runtime.KeepAlive(s) 91 | 92 | } 93 | func __setStyleBorder(o inkwasm.Object, s string) 94 | 95 | func _setStyleDisplay(o inkwasm.Object, s string) { 96 | __setStyleDisplay(o, s) 97 | runtime.KeepAlive(s) 98 | 99 | } 100 | func __setStyleDisplay(o inkwasm.Object, s string) 101 | 102 | func _setStyleTop(o inkwasm.Object, s string) { 103 | __setStyleTop(o, s) 104 | runtime.KeepAlive(s) 105 | 106 | } 107 | func __setStyleTop(o inkwasm.Object, s string) 108 | 109 | func _setStyleLeft(o inkwasm.Object, s string) { 110 | __setStyleLeft(o, s) 111 | runtime.KeepAlive(s) 112 | 113 | } 114 | func __setStyleLeft(o inkwasm.Object, s string) 115 | 116 | func _setSrc(o inkwasm.Object, s string) { 117 | __setSrc(o, s) 118 | runtime.KeepAlive(s) 119 | 120 | } 121 | func __setSrc(o inkwasm.Object, s string) 122 | -------------------------------------------------------------------------------- /webview/sys_js_wasm.s: -------------------------------------------------------------------------------- 1 | // Code generated by INKWASM BUILD; DO NOT EDIT 2 | #include "textflag.h" 3 | 4 | TEXT ·__log(SB), NOSPLIT, $0 5 | CallImport 6 | RET 7 | 8 | TEXT ·log(SB), NOSPLIT, $0 9 | JMP ·_log(SB) 10 | RET 11 | 12 | TEXT ·__querySelector(SB), NOSPLIT, $0 13 | CallImport 14 | RET 15 | 16 | TEXT ·querySelector(SB), NOSPLIT, $0 17 | JMP ·_querySelector(SB) 18 | RET 19 | 20 | TEXT ·__createElement(SB), NOSPLIT, $0 21 | CallImport 22 | RET 23 | 24 | TEXT ·createElement(SB), NOSPLIT, $0 25 | JMP ·_createElement(SB) 26 | RET 27 | 28 | TEXT ·__setAttribute(SB), NOSPLIT, $0 29 | CallImport 30 | RET 31 | 32 | TEXT ·setAttribute(SB), NOSPLIT, $0 33 | JMP ·_setAttribute(SB) 34 | RET 35 | 36 | TEXT ·__setInnerText(SB), NOSPLIT, $0 37 | CallImport 38 | RET 39 | 40 | TEXT ·setInnerText(SB), NOSPLIT, $0 41 | JMP ·_setInnerText(SB) 42 | RET 43 | 44 | TEXT ·__prepend(SB), NOSPLIT, $0 45 | CallImport 46 | RET 47 | 48 | TEXT ·prepend(SB), NOSPLIT, $0 49 | JMP ·_prepend(SB) 50 | RET 51 | 52 | TEXT ·__removeChild(SB), NOSPLIT, $0 53 | CallImport 54 | RET 55 | 56 | TEXT ·removeChild(SB), NOSPLIT, $0 57 | JMP ·_removeChild(SB) 58 | RET 59 | 60 | TEXT ·__setStyleWidth(SB), NOSPLIT, $0 61 | CallImport 62 | RET 63 | 64 | TEXT ·setStyleWidth(SB), NOSPLIT, $0 65 | JMP ·_setStyleWidth(SB) 66 | RET 67 | 68 | TEXT ·__setStyleHeight(SB), NOSPLIT, $0 69 | CallImport 70 | RET 71 | 72 | TEXT ·setStyleHeight(SB), NOSPLIT, $0 73 | JMP ·_setStyleHeight(SB) 74 | RET 75 | 76 | TEXT ·__setStylePosition(SB), NOSPLIT, $0 77 | CallImport 78 | RET 79 | 80 | TEXT ·setStylePosition(SB), NOSPLIT, $0 81 | JMP ·_setStylePosition(SB) 82 | RET 83 | 84 | TEXT ·__setStyleZIndex(SB), NOSPLIT, $0 85 | CallImport 86 | RET 87 | 88 | TEXT ·setStyleZIndex(SB), NOSPLIT, $0 89 | JMP ·_setStyleZIndex(SB) 90 | RET 91 | 92 | TEXT ·__setStyleBorder(SB), NOSPLIT, $0 93 | CallImport 94 | RET 95 | 96 | TEXT ·setStyleBorder(SB), NOSPLIT, $0 97 | JMP ·_setStyleBorder(SB) 98 | RET 99 | 100 | TEXT ·__setStyleDisplay(SB), NOSPLIT, $0 101 | CallImport 102 | RET 103 | 104 | TEXT ·setStyleDisplay(SB), NOSPLIT, $0 105 | JMP ·_setStyleDisplay(SB) 106 | RET 107 | 108 | TEXT ·__setStyleTop(SB), NOSPLIT, $0 109 | CallImport 110 | RET 111 | 112 | TEXT ·setStyleTop(SB), NOSPLIT, $0 113 | JMP ·_setStyleTop(SB) 114 | RET 115 | 116 | TEXT ·__setStyleLeft(SB), NOSPLIT, $0 117 | CallImport 118 | RET 119 | 120 | TEXT ·setStyleLeft(SB), NOSPLIT, $0 121 | JMP ·_setStyleLeft(SB) 122 | RET 123 | 124 | TEXT ·__setSrc(SB), NOSPLIT, $0 125 | CallImport 126 | RET 127 | 128 | TEXT ·setSrc(SB), NOSPLIT, $0 129 | JMP ·_setSrc(SB) 130 | RET 131 | -------------------------------------------------------------------------------- /webview/sys_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package webview 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/jchv/go-winloader" 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | var ( 13 | _CreateCoreWebView2EnvironmentWithOptions winloader.Proc 14 | _Ole32 = windows.NewLazySystemDLL("ole32.dll") 15 | _Ole32CoTaskMemAlloc = _Ole32.NewProc("CoTaskMemAlloc") 16 | ) 17 | 18 | func init() { 19 | dll, _ := winloader.LoadFromMemory(dllFile) 20 | _CreateCoreWebView2EnvironmentWithOptions = dll.Proc("CreateCoreWebView2EnvironmentWithOptions") 21 | } 22 | 23 | var ( 24 | // referenceHolder prevents GC from releasing the COM object. 25 | referenceHolder = make(map[unsafe.Pointer]struct{}, 64) 26 | ) 27 | 28 | type ( 29 | // _IUnknownVTBL implements IUnknown 30 | _IUnknownVTBL struct { 31 | Query uintptr 32 | Add uintptr 33 | Release uintptr 34 | } 35 | ) 36 | 37 | type ( 38 | // _ICoreWebView2 implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.622.22 39 | _ICoreWebView2 struct { 40 | VTBL *_ICoreWebView2VTBL 41 | } 42 | 43 | // _ICoreWebView2VTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.622.22 44 | _ICoreWebView2VTBL struct { 45 | _IUnknownVTBL 46 | GetSettings uintptr 47 | GetSource uintptr 48 | Navigate uintptr 49 | NavigateToString uintptr 50 | AddNavigationStarting uintptr 51 | RemoveNavigationStarting uintptr 52 | AddContentLoading uintptr 53 | RemoveContentLoading uintptr 54 | AddSourceChanged uintptr 55 | RemoveSourceChanged uintptr 56 | AddHistoryChanged uintptr 57 | RemoveHistoryChanged uintptr 58 | AddNavigationCompleted uintptr 59 | RemoveNavigationCompleted uintptr 60 | AddFrameNavigationStarting uintptr 61 | RemoveFrameNavigationStarting uintptr 62 | AddFrameNavigationCompleted uintptr 63 | RemoveFrameNavigationCompleted uintptr 64 | AddScriptDialogOpening uintptr 65 | RemoveScriptDialogOpening uintptr 66 | AddPermissionRequested uintptr 67 | RemovePermissionRequested uintptr 68 | AddProcessFailed uintptr 69 | RemoveProcessFailed uintptr 70 | AddScriptToExecuteOnDocumentCreated uintptr 71 | RemoveScriptToExecuteOnDocumentCreated uintptr 72 | ExecuteScript uintptr 73 | CapturePreview uintptr 74 | Reload uintptr 75 | PostWebMessageAsJSON uintptr 76 | PostWebMessageAsString uintptr 77 | AddWebMessageReceived uintptr 78 | RemoveWebMessageReceived uintptr 79 | CallDevToolsProtocolMethod uintptr 80 | GetBrowserProcessID uintptr 81 | GetCanGoBack uintptr 82 | GetCanGoForward uintptr 83 | GoBack uintptr 84 | GoForward uintptr 85 | GetDevToolsProtocolEventReceiver uintptr 86 | Stop uintptr 87 | AddNewWindowRequested uintptr 88 | RemoveNewWindowRequested uintptr 89 | AddDocumentTitleChanged uintptr 90 | RemoveDocumentTitleChanged uintptr 91 | GetDocumentTitle uintptr 92 | AddHostObjectToScript uintptr 93 | RemoveHostObjectFromScript uintptr 94 | OpenDevToolsWindow uintptr 95 | AddContainsFullScreenElementChanged uintptr 96 | RemoveContainsFullScreenElementChanged uintptr 97 | GetContainsFullScreenElement uintptr 98 | AddWebResourceRequested uintptr 99 | RemoveWebResourceRequested uintptr 100 | AddWebResourceRequestedFilter uintptr 101 | RemoveWebResourceRequestedFilter uintptr 102 | AddWindowCloseRequested uintptr 103 | RemoveWindowCloseRequested uintptr 104 | } 105 | ) 106 | 107 | var _GUIDCoreWebView22, _ = windows.GUIDFromString("{9E8F0CF8-E670-4B5E-B2BC-73E061E3184C}") 108 | 109 | type ( 110 | // ICoreWebView22 implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_2?view=webview2-1.0.622.22 111 | _ICoreWebView22 struct { 112 | VTBL *ICoreWebView22VTBL 113 | } 114 | 115 | // ICoreWebView22VTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_2?view=webview2-1.0.622.22 116 | ICoreWebView22VTBL struct { 117 | _ICoreWebView2VTBL 118 | AddWebResourceResponseReceived uintptr 119 | RemoveWebResourceResponseReceived uintptr 120 | NavigateWithWebResourceRequest uintptr 121 | AddDOMContentLoaded uintptr 122 | RemoveDOMContentLoaded uintptr 123 | CookieManager uintptr 124 | Environment uintptr 125 | } 126 | ) 127 | 128 | type ( 129 | // _ICoreWebView2Environment implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment 130 | _ICoreWebView2Environment struct { 131 | VTBL *_ICoreWebView2EnvironmentVTBL 132 | } 133 | 134 | // _ICoreWebView2EnvironmentVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment 135 | _ICoreWebView2EnvironmentVTBL struct { 136 | _IUnknownVTBL 137 | CreateCoreWebView2Controller uintptr 138 | CreateWebResourceResponse uintptr 139 | GetBrowserVersionString uintptr 140 | AddNewBrowserVersionAvailable uintptr 141 | RemoveNewBrowserVersionAvailable uintptr 142 | } 143 | ) 144 | 145 | type ( 146 | // _ICoreWebView2Controller implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2controller?view=webview2-1.0.622.22 147 | _ICoreWebView2Controller struct { 148 | VTBL *_ICoreWebView2ControllerVTBL 149 | } 150 | 151 | // _ICoreWebView2ControllerVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2controller?view=webview2-1.0.622.22 152 | _ICoreWebView2ControllerVTBL struct { 153 | _IUnknownVTBL 154 | GetIsVisible uintptr 155 | PutIsVisible uintptr 156 | GetBounds uintptr 157 | PutBounds uintptr 158 | GetZoomFactor uintptr 159 | PutZoomFactor uintptr 160 | AddZoomFactorChanged uintptr 161 | RemoveZoomFactorChanged uintptr 162 | SetBoundsAndZoomFactor uintptr 163 | MoveFocus uintptr 164 | AddMoveFocusRequested uintptr 165 | RemoveMoveFocusRequested uintptr 166 | AddGotFocus uintptr 167 | RemoveGotFocus uintptr 168 | AddLostFocus uintptr 169 | RemoveLostFocus uintptr 170 | AddAcceleratorKeyPressed uintptr 171 | RemoveAcceleratorKeyPressed uintptr 172 | GetParentWindow uintptr 173 | PutParentWindow uintptr 174 | NotifyParentWindowPositionChanged uintptr 175 | Close uintptr 176 | GetCoreWebView2 uintptr 177 | } 178 | ) 179 | 180 | type ( 181 | // _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2createcorewebview2environmentcompletedhandler. 182 | _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler struct { 183 | VTBL *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL 184 | 185 | Counter uintptr 186 | Invoke func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, err uintptr, val *_ICoreWebView2Environment) uintptr 187 | } 188 | 189 | // _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2createcorewebview2environmentcompletedhandler. 190 | _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL struct { 191 | _IUnknownVTBL 192 | Invoke uintptr 193 | } 194 | ) 195 | 196 | var _CoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL = &_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL{ 197 | _IUnknownVTBL: _IUnknownVTBL{ 198 | Query: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, _, o uintptr) uintptr { 199 | return 0 200 | }), 201 | Add: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr { 202 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 203 | this.Counter += 1 204 | return this.Counter 205 | }), 206 | Release: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr { 207 | this.Counter -= 1 208 | if this.Counter == 0 { 209 | delete(referenceHolder, unsafe.Pointer(this)) 210 | *this = _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{} 211 | } 212 | return this.Counter + 1 213 | }), 214 | }, 215 | Invoke: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, err uintptr, val *_ICoreWebView2Environment) uintptr { 216 | if this == nil { 217 | return 0 218 | } 219 | return this.Invoke(this, err, val) 220 | }), 221 | } 222 | 223 | type ( 224 | // _ICoreWebView2CreateCoreWebView2ControllerCompletedHandler implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2createcorewebview2controllercompletedhandler 225 | _ICoreWebView2CreateCoreWebView2ControllerCompletedHandler struct { 226 | VTBL *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL 227 | 228 | Counter uintptr 229 | Invoke func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, err uintptr, val *_ICoreWebView2Controller) uintptr 230 | } 231 | 232 | // _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2createcorewebview2controllercompletedhandler 233 | _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL struct { 234 | _IUnknownVTBL 235 | Invoke uintptr 236 | } 237 | ) 238 | 239 | var _CoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL = &_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL{ 240 | _IUnknownVTBL: _IUnknownVTBL{ 241 | Query: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, _, o uintptr) uintptr { 242 | return 0 243 | }), 244 | Add: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr { 245 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 246 | this.Counter += 1 247 | return this.Counter 248 | }), 249 | Release: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr { 250 | this.Counter -= 1 251 | if this.Counter == 0 { 252 | delete(referenceHolder, unsafe.Pointer(this)) 253 | *this = _ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{} 254 | } 255 | return this.Counter + 1 256 | }), 257 | }, 258 | Invoke: windows.NewCallback(func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, err uintptr, val *_ICoreWebView2Controller) uintptr { 259 | if this == nil { 260 | return 0 261 | } 262 | return this.Invoke(this, err, val) 263 | }), 264 | } 265 | 266 | type ( 267 | // _ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2addscripttoexecuteondocumentcreatedcompletedhandler?view=webview2-1.0.1264.42 268 | _ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler struct { 269 | VTBL *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVTBL 270 | 271 | Counter uintptr 272 | Invoke func(this *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler, err uintptr, id uintptr) uintptr 273 | } 274 | 275 | // _ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2addscripttoexecuteondocumentcreatedcompletedhandler?view=webview2-1.0.1264.42 276 | _ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVTBL struct { 277 | _IUnknownVTBL 278 | Invoke uintptr 279 | } 280 | ) 281 | 282 | var _CoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVTBL = &_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVTBL{ 283 | _IUnknownVTBL: _IUnknownVTBL{ 284 | Query: windows.NewCallback(func(this *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler, _, o uintptr) uintptr { 285 | return 0 286 | }), 287 | Add: windows.NewCallback(func(this *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler) uintptr { 288 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 289 | this.Counter += 1 290 | return this.Counter 291 | }), 292 | Release: windows.NewCallback(func(this *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler) uintptr { 293 | this.Counter -= 1 294 | if this.Counter == 0 { 295 | delete(referenceHolder, unsafe.Pointer(this)) 296 | *this = _ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler{} 297 | } 298 | return this.Counter + 1 299 | }), 300 | }, 301 | Invoke: windows.NewCallback(func(this *_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler, err uintptr, id uintptr) uintptr { 302 | if this == nil { 303 | return 0 304 | } 305 | return this.Invoke(this, err, id) 306 | }), 307 | } 308 | 309 | type ( 310 | // _ICoreWebView2CookieManager implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2cookiemanager 311 | _ICoreWebView2CookieManager struct { 312 | VTBL *_ICoreWebView2CookieManagerVTBL 313 | } 314 | 315 | // _ICoreWebView2CookieManagerVTBL implements https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2cookiemanager 316 | _ICoreWebView2CookieManagerVTBL struct { 317 | _IUnknownVTBL 318 | CreateCookie uintptr 319 | CopyCookie uintptr 320 | GetCookies uintptr 321 | AddOrUpdateCookie uintptr 322 | DeleteCookie uintptr 323 | DeleteCookies uintptr 324 | DeleteCookiesWithDomainAndPath uintptr 325 | DeleteAllCookies uintptr 326 | } 327 | ) 328 | 329 | type ( 330 | _ICoreWebView2GetCookiesCompletedHandler struct { 331 | VTBL *_ICoreWebView2GetCookiesCompletedHandlerVTBL 332 | 333 | Counter uintptr 334 | Invoke func(this *_ICoreWebView2GetCookiesCompletedHandler, err uintptr, cookies *_ICoreWebView2CookieList) uintptr 335 | } 336 | 337 | _ICoreWebView2GetCookiesCompletedHandlerVTBL struct { 338 | _IUnknownVTBL 339 | Invoke uintptr 340 | } 341 | ) 342 | 343 | var _CoreWebView2GetCookiesCompletedHandlerVTBL = &_ICoreWebView2GetCookiesCompletedHandlerVTBL{ 344 | _IUnknownVTBL: _IUnknownVTBL{ 345 | Query: windows.NewCallback(func(this *_ICoreWebView2GetCookiesCompletedHandler, _, o uintptr) uintptr { 346 | return 0 347 | }), 348 | Add: windows.NewCallback(func(this *_ICoreWebView2GetCookiesCompletedHandler) uintptr { 349 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 350 | this.Counter += 1 351 | return this.Counter 352 | }), 353 | Release: windows.NewCallback(func(this *_ICoreWebView2GetCookiesCompletedHandler) uintptr { 354 | this.Counter -= 1 355 | if this.Counter == 0 { 356 | delete(referenceHolder, unsafe.Pointer(this)) 357 | *this = _ICoreWebView2GetCookiesCompletedHandler{} 358 | } 359 | return this.Counter + 1 360 | }), 361 | }, 362 | Invoke: windows.NewCallback(func(this *_ICoreWebView2GetCookiesCompletedHandler, err uintptr, cookies *_ICoreWebView2CookieList) uintptr { 363 | if this == nil { 364 | return 0 365 | } 366 | return this.Invoke(this, err, cookies) 367 | }), 368 | } 369 | 370 | type ( 371 | _ICoreWebView2CookieList struct { 372 | VTBL *_ICoreWebView2CookieListVTBL 373 | } 374 | 375 | _ICoreWebView2CookieListVTBL struct { 376 | _IUnknownVTBL 377 | Count uintptr 378 | GetValueAtIndex uintptr 379 | } 380 | ) 381 | 382 | type ( 383 | _ICoreWebView2Cookie struct { 384 | VTBL *_ICoreWebView2CookieVTBL 385 | } 386 | 387 | _ICoreWebView2CookieVTBL struct { 388 | _IUnknownVTBL 389 | GetName uintptr 390 | GetValue uintptr 391 | PutValue uintptr 392 | GetDomain uintptr 393 | GetPath uintptr 394 | GetExpires uintptr 395 | PutExpires uintptr 396 | IsHttpOnly uintptr 397 | PutHttpOnly uintptr 398 | GetSameSite uintptr 399 | PutSameSite uintptr 400 | IsSecure uintptr 401 | PutSecure uintptr 402 | IsSession uintptr 403 | } 404 | ) 405 | 406 | type ( 407 | _ICoreWebView2FrameWebMessageReceivedEventHandler struct { 408 | VTBL *_ICoreWebView2FrameWebMessageReceivedEventHandlerVTBL 409 | 410 | Counter uintptr 411 | Invoke func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler, frame uintptr, args *_ICoreWebView2WebMessageReceivedEventArgs) uintptr 412 | } 413 | 414 | _ICoreWebView2FrameWebMessageReceivedEventHandlerVTBL struct { 415 | _IUnknownVTBL 416 | Invoke uintptr 417 | } 418 | ) 419 | 420 | var ( 421 | _CoreWebView2FrameWebMessageReceivedEventHandlerVTBL = &_ICoreWebView2FrameWebMessageReceivedEventHandlerVTBL{ 422 | _IUnknownVTBL: _IUnknownVTBL{ 423 | Query: windows.NewCallback(func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler, _, o uintptr) uintptr { 424 | return 0 425 | }), 426 | Add: windows.NewCallback(func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler) uintptr { 427 | this.Counter += 1 428 | return this.Counter 429 | }), 430 | Release: windows.NewCallback(func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler) uintptr { 431 | this.Counter -= 1 432 | if this.Counter == 0 { 433 | *this = _ICoreWebView2FrameWebMessageReceivedEventHandler{} 434 | } 435 | return this.Counter + 1 436 | }), 437 | }, 438 | Invoke: windows.NewCallback(func(this *_ICoreWebView2FrameWebMessageReceivedEventHandler, frame uintptr, args *_ICoreWebView2WebMessageReceivedEventArgs) uintptr { 439 | if this == nil { 440 | return 0 441 | } 442 | return this.Invoke(this, frame, args) 443 | }), 444 | } 445 | ) 446 | 447 | type ( 448 | _ICoreWebView2WebMessageReceivedEventArgs struct { 449 | VTBL *_ICoreWebView2WebMessageReceivedEventArgsVTBL 450 | } 451 | 452 | _ICoreWebView2WebMessageReceivedEventArgsVTBL struct { 453 | _IUnknownVTBL 454 | Source uintptr 455 | WebMessageAsJson uintptr 456 | TryGetWebMessageAsString uintptr 457 | } 458 | ) 459 | 460 | type ( 461 | _ICoreWebView2ExecuteScriptCompletedHandler struct { 462 | VTBL *_ICoreWebView2ExecuteScriptCompletedHandlerVTBL 463 | 464 | Counter uintptr 465 | Invoke func(this *_ICoreWebView2ExecuteScriptCompletedHandler, err uintptr, resultObjectAsJson uintptr) uintptr 466 | } 467 | 468 | _ICoreWebView2ExecuteScriptCompletedHandlerVTBL struct { 469 | _IUnknownVTBL 470 | Invoke uintptr 471 | } 472 | ) 473 | 474 | var _CoreWebView2ExecuteScriptCompletedHandlerVTBL = &_ICoreWebView2ExecuteScriptCompletedHandlerVTBL{ 475 | _IUnknownVTBL: _IUnknownVTBL{ 476 | Query: windows.NewCallback(func(this *_ICoreWebView2ExecuteScriptCompletedHandler, _, o uintptr) uintptr { 477 | return 0 478 | }), 479 | Add: windows.NewCallback(func(this *_ICoreWebView2ExecuteScriptCompletedHandler) uintptr { 480 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 481 | this.Counter += 1 482 | return this.Counter 483 | }), 484 | Release: windows.NewCallback(func(this *_ICoreWebView2ExecuteScriptCompletedHandler) uintptr { 485 | this.Counter -= 1 486 | if this.Counter == 0 { 487 | delete(referenceHolder, unsafe.Pointer(this)) 488 | *this = _ICoreWebView2ExecuteScriptCompletedHandler{} 489 | } 490 | return this.Counter + 1 491 | }), 492 | }, 493 | Invoke: windows.NewCallback(func(this *_ICoreWebView2ExecuteScriptCompletedHandler, err uintptr, resultObjectAsJson uintptr) uintptr { 494 | if this == nil { 495 | return 0 496 | } 497 | return this.Invoke(this, err, resultObjectAsJson) 498 | }), 499 | } 500 | 501 | type ( 502 | _ICoreWebView2DocumentTitleChangedEventHandler struct { 503 | VTBL *_ICoreWebView2DocumentTitleChangedEventHandlerVTBL 504 | 505 | Counter uintptr 506 | Invoke func(this *_ICoreWebView2DocumentTitleChangedEventHandler, w *_ICoreWebView2, v uintptr) uintptr 507 | } 508 | 509 | _ICoreWebView2DocumentTitleChangedEventHandlerVTBL struct { 510 | _IUnknownVTBL 511 | Invoke uintptr 512 | } 513 | ) 514 | 515 | var _CoreWebView2DocumentTitleChangedEventHandlerVTBL = &_ICoreWebView2DocumentTitleChangedEventHandlerVTBL{ 516 | _IUnknownVTBL: _IUnknownVTBL{ 517 | Query: windows.NewCallback(func(this *_ICoreWebView2DocumentTitleChangedEventHandler, _, o uintptr) uintptr { 518 | return 0 519 | }), 520 | Add: windows.NewCallback(func(this *_ICoreWebView2DocumentTitleChangedEventHandler) uintptr { 521 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 522 | this.Counter += 1 523 | return this.Counter 524 | }), 525 | Release: windows.NewCallback(func(this *_ICoreWebView2DocumentTitleChangedEventHandler) uintptr { 526 | this.Counter -= 1 527 | if this.Counter == 0 { 528 | delete(referenceHolder, unsafe.Pointer(this)) 529 | *this = _ICoreWebView2DocumentTitleChangedEventHandler{} 530 | } 531 | return this.Counter + 1 532 | }), 533 | }, 534 | Invoke: windows.NewCallback(func(this *_ICoreWebView2DocumentTitleChangedEventHandler, w *_ICoreWebView2, v uintptr) uintptr { 535 | if this == nil { 536 | return 0 537 | } 538 | return this.Invoke(this, w, v) 539 | }), 540 | } 541 | 542 | type ( 543 | _ICoreWebView2SourceChangedEventHandler struct { 544 | VTBL *_ICoreWebView2SourceChangedEventHandlerVTBL 545 | 546 | Counter uintptr 547 | Invoke func(this *_ICoreWebView2SourceChangedEventHandler, w *_ICoreWebView2, v uintptr) uintptr 548 | } 549 | 550 | _ICoreWebView2SourceChangedEventHandlerVTBL struct { 551 | _IUnknownVTBL 552 | Invoke uintptr 553 | } 554 | ) 555 | 556 | var _CoreWebView2SourceChangedEventHandlerVTBL = &_ICoreWebView2SourceChangedEventHandlerVTBL{ 557 | _IUnknownVTBL: _IUnknownVTBL{ 558 | Query: windows.NewCallback(func(this *_ICoreWebView2SourceChangedEventHandler, _, o uintptr) uintptr { 559 | return 0 560 | }), 561 | Add: windows.NewCallback(func(this *_ICoreWebView2SourceChangedEventHandler) uintptr { 562 | referenceHolder[unsafe.Pointer(this)] = struct{}{} 563 | this.Counter += 1 564 | return this.Counter 565 | }), 566 | Release: windows.NewCallback(func(this *_ICoreWebView2SourceChangedEventHandler) uintptr { 567 | this.Counter -= 1 568 | if this.Counter == 0 { 569 | delete(referenceHolder, unsafe.Pointer(this)) 570 | *this = _ICoreWebView2SourceChangedEventHandler{} 571 | } 572 | return this.Counter + 1 573 | }), 574 | }, 575 | Invoke: windows.NewCallback(func(this *_ICoreWebView2SourceChangedEventHandler, w *_ICoreWebView2, v uintptr) uintptr { 576 | if this == nil { 577 | return 0 578 | } 579 | return this.Invoke(this, w, v) 580 | }), 581 | } 582 | -------------------------------------------------------------------------------- /webview/sys_windows_386.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkeliz/giowebview/dd13eba9780e94f9623fb4e1282f3462d4565de7/webview/sys_windows_386.dll -------------------------------------------------------------------------------- /webview/sys_windows_386.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * The name of Microsoft Corporation, or the names of its contributors 15 | may not be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | package webview 32 | 33 | import ( 34 | _ "embed" 35 | ) 36 | 37 | //go:embed "sys_windows_386.dll" 38 | var dllFile []byte 39 | 40 | var registryPaths = []string{`SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}`} 41 | -------------------------------------------------------------------------------- /webview/sys_windows_amd64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkeliz/giowebview/dd13eba9780e94f9623fb4e1282f3462d4565de7/webview/sys_windows_amd64.dll -------------------------------------------------------------------------------- /webview/sys_windows_amd64.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * The name of Microsoft Corporation, or the names of its contributors 15 | may not be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | package webview 32 | 33 | import ( 34 | _ "embed" 35 | ) 36 | 37 | //go:embed "sys_windows_amd64.dll" 38 | var dllFile []byte 39 | 40 | var registryPaths = []string{`SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}`} 41 | -------------------------------------------------------------------------------- /webview/sys_windows_arm64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkeliz/giowebview/dd13eba9780e94f9623fb4e1282f3462d4565de7/webview/sys_windows_arm64.dll -------------------------------------------------------------------------------- /webview/sys_windows_arm64.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * The name of Microsoft Corporation, or the names of its contributors 15 | may not be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | package webview 32 | 33 | import ( 34 | _ "embed" 35 | ) 36 | 37 | //go:embed "sys_windows_arm64.dll" 38 | var dllFile []byte 39 | 40 | var registryPaths = []string{`SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}`} 41 | -------------------------------------------------------------------------------- /webview/webview.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | //go:generate javac -source 8 -target 8 -bootclasspath $ANDROID_HOME\platforms\android-30\android.jar -d $TEMP\gowebview\classes sys_android.java 4 | //go:generate jar cf sys_android.jar -C $TEMP\gowebview\classes . 5 | 6 | import ( 7 | "net/url" 8 | ) 9 | 10 | // WebView is a webview. 11 | type WebView interface { 12 | // Configure sets the configuration for the webview. 13 | Configure(config Config) 14 | 15 | // Resize resizes the webview to the specified size. 16 | // To make it invisible, set the size to (0, 0). 17 | // To make it visible, set the size to a non-zero value. 18 | // 19 | // The offset is the position of the webview relative to window coordinates, 20 | // assuming 0,0 as the top-left corner of the window. 21 | Resize(size Point, offset Point) 22 | 23 | // Navigate navigates to the specified URL. 24 | Navigate(url *url.URL) 25 | 26 | // Close closes and terminate the webview. 27 | Close() 28 | 29 | // Events returns actions that occur on the WebView. 30 | Events() chan Event 31 | 32 | // DataManager returns the DataManager for the webview. 33 | DataManager() DataManager 34 | 35 | // JavascriptManager returns the JavascriptManager for the webview. 36 | JavascriptManager() JavascriptManager 37 | } 38 | 39 | type internalWebView interface { 40 | attach(w *webview) error 41 | configure(w *webview, config Config) 42 | resize(w *webview, pos [4]float32) 43 | navigate(w *webview, url *url.URL) 44 | close(w *webview) 45 | } 46 | 47 | // Point is a point in the coordinate system. 48 | type Point struct { 49 | X, Y float32 50 | } 51 | 52 | // NewWebView creates a new webview. 53 | func NewWebView(config Config) (WebView, error) { 54 | return newWebview(config) 55 | } 56 | 57 | const ( 58 | idMethodStart int = iota 59 | idMethodResize 60 | idMethodConfig 61 | idMethodNavigate 62 | idMethodClose 63 | ) 64 | -------------------------------------------------------------------------------- /webview/webview_all.go: -------------------------------------------------------------------------------- 1 | //go:build ios || darwin || windows || js || android 2 | 3 | package webview 4 | 5 | import ( 6 | "net/url" 7 | "sync" 8 | 9 | "github.com/inkeliz/giowebview/webview/internal" 10 | ) 11 | 12 | // webview implements the WebView interface. 13 | // The driver is different for each platform. 14 | type webview struct { 15 | handle internal.Handle 16 | driver *driver 17 | 18 | mutex sync.Mutex 19 | scheduler internal.Scheduler 20 | fan internal.Fan[Event] 21 | 22 | lastPos [4]float32 23 | visible bool 24 | closed bool 25 | 26 | javascriptManager JavascriptManager 27 | dataManager DataManager 28 | } 29 | 30 | func newWebview(config Config) (*webview, error) { 31 | w := &webview{driver: &driver{config: config}} 32 | w.handle = internal.NewHandle(w) 33 | 34 | if err := w.driver.attach(w); err != nil { 35 | return nil, err 36 | } 37 | 38 | return w, nil 39 | } 40 | 41 | // Configure implements the WebView interface. 42 | func (w *webview) Configure(config Config) { 43 | w.mutex.Lock() 44 | defer w.mutex.Unlock() 45 | if w.closed { 46 | return 47 | } 48 | 49 | w.driver.configure(w, config) 50 | } 51 | 52 | // Resize implements the WebView interface. 53 | func (w *webview) Resize(size Point, offset Point) { 54 | pos := [4]float32{offset.X, offset.Y, size.X, size.Y} 55 | if w.lastPos == pos { 56 | return 57 | } 58 | w.lastPos = pos 59 | 60 | w.scheduler.Run(idMethodResize, func() { 61 | w.mutex.Lock() 62 | defer w.mutex.Unlock() 63 | if w.closed { 64 | return 65 | } 66 | 67 | w.driver.resize(w, pos) 68 | }) 69 | } 70 | 71 | // Navigate implements the WebView interface. 72 | func (w *webview) Navigate(url *url.URL) { 73 | w.scheduler.Run(idMethodNavigate, func() { 74 | w.mutex.Lock() 75 | defer w.mutex.Unlock() 76 | if w.closed { 77 | return 78 | } 79 | 80 | w.driver.navigate(w, url) 81 | }) 82 | } 83 | 84 | // Close implements the WebView interface. 85 | func (w *webview) Close() { 86 | w.scheduler.Run(idMethodClose, func() { 87 | w.mutex.Lock() 88 | defer w.mutex.Unlock() 89 | if w.closed { 90 | return 91 | } 92 | 93 | w.driver.close(w) 94 | w.fan.Close() 95 | w.closed = true 96 | }) 97 | } 98 | 99 | // Events implements the WebView interface. 100 | func (w *webview) Events() chan Event { return w.fan.Add() } 101 | 102 | // DataManager implements the WebView interface. 103 | func (w *webview) DataManager() DataManager { return w.dataManager } 104 | 105 | // JavascriptManager implements the WebView interface. 106 | func (w *webview) JavascriptManager() JavascriptManager { return w.javascriptManager } 107 | -------------------------------------------------------------------------------- /webview/webview_android.go: -------------------------------------------------------------------------------- 1 | //go:build android 2 | // +build android 3 | 4 | package webview 5 | 6 | import ( 7 | "net/url" 8 | 9 | "git.wow.st/gmp/jni" 10 | ) 11 | 12 | type driver struct { 13 | config Config 14 | 15 | clsWebView jni.Class 16 | objWebView jni.Object 17 | } 18 | 19 | func (r *driver) attach(w *webview) (err error) { 20 | w.scheduler.SetRunner(w.driver.config.RunOnMain) 21 | 22 | w.driver.config.RunOnMain(func() { 23 | err = jni.Do(r.config.VM, func(env jni.Env) error { 24 | cls, err := jni.LoadClass(env, jni.ClassLoaderFor(env, w.driver.config.Context), "com/inkeliz/webview/sys_android") 25 | if err != nil { 26 | return err 27 | } 28 | 29 | // [Android] We need to create an GlobalRef of our class, otherwise we can't manipulate that afterwards. 30 | r.clsWebView = jni.Class(jni.NewGlobalRef(env, jni.Object(cls))) 31 | 32 | obj, err := jni.NewObject(env, r.clsWebView, jni.GetMethodID(env, r.clsWebView, "", `()V`)) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | // [Android] We need to create an GlobalRef of our class, otherwise we can't manipulate that afterwards. 38 | r.objWebView = jni.Object(jni.NewGlobalRef(env, obj)) 39 | 40 | err = r.callArgs("webview_create", "(Landroid/view/View;J)V", func(env jni.Env) []jni.Value { 41 | return []jni.Value{ 42 | jni.Value(r.config.View), 43 | jni.Value(uintptr(w.handle)), 44 | } 45 | }) 46 | 47 | if err != nil { 48 | return err 49 | } 50 | 51 | if err := r.setProxy(); err != nil { 52 | return err 53 | } 54 | 55 | if err := r.setCerts(); err != nil { 56 | return err 57 | } 58 | 59 | w.javascriptManager = newJavascriptManager(w) 60 | w.dataManager = newDataManager(w) 61 | 62 | return nil 63 | }) 64 | }) 65 | 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return nil 71 | } 72 | 73 | func (r *driver) configure(w *webview, config Config) { 74 | r.config = config 75 | w.scheduler.SetRunner(w.driver.config.RunOnMain) 76 | } 77 | 78 | func (r *driver) resize(w *webview, pos [4]float32) { 79 | if pos[2] == 0 && pos[3] == 0 { 80 | if w.visible { 81 | r.call("webview_hide", "()V") 82 | w.visible = false 83 | } 84 | } else { 85 | r.callArgs("webview_resize", "(IIII)V", func(env jni.Env) []jni.Value { 86 | return []jni.Value{ 87 | jni.Value(int32(pos[0] + 0.5)), 88 | jni.Value(int32(pos[1] + 0.5)), 89 | jni.Value(int32(pos[2] + 0.5)), 90 | jni.Value(int32(pos[3] + 0.5)), 91 | } 92 | }) 93 | if !w.visible { 94 | r.call("webview_show", "()V") 95 | w.visible = true 96 | } 97 | } 98 | } 99 | 100 | func (r *driver) navigate(w *webview, url *url.URL) { 101 | r.callArgs("webview_navigate", "(Ljava/lang/String;)V", func(env jni.Env) []jni.Value { 102 | return []jni.Value{ 103 | jni.Value(jni.JavaString(env, url.String())), 104 | } 105 | }) 106 | } 107 | 108 | func (r *driver) close(w *webview) { 109 | if r.objWebView == 0 || r.clsWebView == 0 { 110 | return 111 | } 112 | 113 | r.call("webview_destroy", "()V") 114 | 115 | go jni.Do(r.config.VM, func(env jni.Env) error { 116 | jni.DeleteGlobalRef(env, jni.Object(r.clsWebView)) 117 | jni.DeleteGlobalRef(env, r.objWebView) 118 | 119 | return nil 120 | }) 121 | 122 | r.objWebView, r.clsWebView = 0, 0 123 | } 124 | 125 | func (r *driver) call(name, sig string) (err error) { 126 | // The arguments may need the `env` 127 | // In that case there's no input, so it's using func(env jni.Env) []jni.Value { return nil } instead 128 | return r.callArgs(name, sig, func(env jni.Env) []jni.Value { return nil }) 129 | } 130 | 131 | func (r *driver) callArgs(name, sig string, args func(env jni.Env) []jni.Value) (err error) { 132 | err = jni.Do(r.config.VM, func(env jni.Env) error { 133 | return jni.CallVoidMethod(env, r.objWebView, jni.GetMethodID(env, r.clsWebView, name, sig), args(env)...) 134 | }) 135 | return err 136 | } 137 | 138 | func (r *driver) callBooleanArgs(name, sig string, args func(env jni.Env) []jni.Value) (b bool, err error) { 139 | err = jni.Do(r.config.VM, func(env jni.Env) error { 140 | b, err = jni.CallBooleanMethod(env, r.objWebView, jni.GetMethodID(env, r.clsWebView, name, sig), args(env)...) 141 | return err 142 | }) 143 | return b, err 144 | } 145 | -------------------------------------------------------------------------------- /webview/webview_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build ios || darwin 2 | 3 | package webview 4 | 5 | /* 6 | #cgo CFLAGS: -xobjective-c -fmodules -fobjc-arc 7 | 8 | #include 9 | #import 10 | 11 | extern CFTypeRef config(); 12 | extern CFTypeRef create(CFTypeRef config, uintptr_t handler); 13 | extern void resize(CFTypeRef web, CFTypeRef windowRef, float x, float y, float w, float h); 14 | extern void run(CFTypeRef web, CFTypeRef windowRef); 15 | extern void seturl(CFTypeRef web, char *u); 16 | extern void hide(CFTypeRef web); 17 | extern void show(CFTypeRef web); 18 | 19 | void webview_cf_release(CFTypeRef obj) { 20 | CFRelease(obj); 21 | } 22 | 23 | */ 24 | import "C" 25 | import ( 26 | "net/url" 27 | "unsafe" 28 | ) 29 | 30 | type driver struct { 31 | config Config 32 | 33 | webviewObject C.CFTypeRef 34 | webviewConfig C.CFTypeRef 35 | } 36 | 37 | func (r *driver) attach(w *webview) (err error) { 38 | defer w.scheduler.SetRunner(w.driver.config.RunOnMain) 39 | 40 | r.config.RunOnMain(func() { 41 | w.mutex.Lock() 42 | defer w.mutex.Unlock() 43 | 44 | r.webviewConfig = C.config() 45 | r.webviewObject = C.create(r.webviewConfig, C.uintptr_t(uintptr(w.handle))) 46 | 47 | w.javascriptManager = newJavascriptManager(w) 48 | w.dataManager = newDataManager(w) 49 | 50 | C.run(r.webviewObject, C.CFTypeRef(r.config.View)) 51 | }) 52 | 53 | return nil 54 | } 55 | 56 | func (r *driver) resize(w *webview, pos [4]float32) { 57 | if pos[2] == 0 && pos[3] == 0 { 58 | if w.visible { 59 | C.hide(r.webviewObject) 60 | w.visible = false 61 | } 62 | } else { 63 | C.resize( 64 | r.webviewObject, 65 | C.CFTypeRef(r.config.View), 66 | C.float(pos[0]/r.config.PxPerDp), 67 | C.float(pos[1]/r.config.PxPerDp), 68 | C.float(pos[2]/r.config.PxPerDp), 69 | C.float(pos[3]/r.config.PxPerDp), 70 | ) 71 | if !w.visible { 72 | C.show(r.webviewObject) 73 | w.visible = true 74 | } 75 | } 76 | } 77 | 78 | func (r *driver) configure(w *webview, config Config) { 79 | r.config = config 80 | w.scheduler.SetRunner(w.driver.config.RunOnMain) 81 | } 82 | 83 | func (r *driver) navigate(w *webview, url *url.URL) { 84 | u := C.CString(url.String()) 85 | defer C.free(unsafe.Pointer(u)) 86 | 87 | C.seturl(r.webviewObject, u) 88 | } 89 | 90 | func (r *driver) close(w *webview) { 91 | C.webview_cf_release(r.webviewObject) 92 | C.webview_cf_release(r.webviewConfig) 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /webview/webview_js.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | 7 | "github.com/inkeliz/go_inkwasm/inkwasm" 8 | ) 9 | 10 | type driver struct { 11 | config Config 12 | iframe inkwasm.Object 13 | } 14 | 15 | func (r *driver) attach(w *webview) error { 16 | defer w.scheduler.SetRunner(r.config.RunOnMain) 17 | 18 | r.iframe = createElement("iframe") 19 | setStyleDisplay(r.iframe, "none") 20 | setStylePosition(r.iframe, "fixed") 21 | setStyleZIndex(r.iframe, "1") 22 | setStyleBorder(r.iframe, "none") 23 | prepend(r.config.Element, r.iframe) 24 | 25 | return nil 26 | } 27 | 28 | func (r *driver) configure(w *webview, config Config) { 29 | r.config = config 30 | } 31 | 32 | func (r *driver) resize(w *webview, pos [4]float32) { 33 | scale := r.config.PxPerDp 34 | 35 | if pos[2] == 0 && pos[3] == 0 { 36 | if w.visible { 37 | setStyleDisplay(r.iframe, "none") 38 | w.visible = false 39 | } 40 | } else { 41 | pos := [4]int{int((pos[0] + 0.5) / scale), int((pos[1] + 0.5) / scale), int((pos[2] + 0.5) / scale), int((pos[3] + 0.5) / scale)} 42 | for i, v := range []func(object inkwasm.Object, v string){setStyleLeft, setStyleTop, setStyleWidth, setStyleHeight} { 43 | v(r.iframe, fmt.Sprintf("%dpx", pos[i])) 44 | } 45 | 46 | if !w.visible { 47 | setStyleDisplay(r.iframe, "block") 48 | w.visible = true 49 | } 50 | } 51 | } 52 | 53 | func (r *driver) navigate(w *webview, url *url.URL) { 54 | setSrc(r.iframe, url.String()) 55 | } 56 | 57 | func (r *driver) close(w *webview) { 58 | removeChild(r.config.Element, r.iframe) 59 | } 60 | -------------------------------------------------------------------------------- /webview/webview_unsupported.go: -------------------------------------------------------------------------------- 1 | //go:build !android && !darwin && !ios && !windows && !(js && wasm) 2 | 3 | package webview 4 | 5 | type webview struct{} 6 | 7 | func newWebview(config Config) (*webview, error) { 8 | return nil, ErrNotSupported 9 | } 10 | -------------------------------------------------------------------------------- /webview/webview_windows.go: -------------------------------------------------------------------------------- 1 | package webview 2 | 3 | import ( 4 | "net/url" 5 | "os" 6 | "sync/atomic" 7 | "syscall" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | "golang.org/x/sys/windows/registry" 12 | ) 13 | 14 | func init() { 15 | for _, s := range []string{"WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", "WEBVIEW2_USER_DATA_FOLDER", "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "WEBVIEW2_RELEASE_CHANNEL_PREFERENCE"} { 16 | os.Setenv(s, "") 17 | } 18 | } 19 | 20 | type driver struct { 21 | config Config 22 | active uint32 23 | 24 | controllerCompletedHandler *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler 25 | environmentCompletedHandler *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler 26 | controller *_ICoreWebView2Controller 27 | webview2 *_ICoreWebView2 28 | webview22 *_ICoreWebView22 29 | 30 | callbackTitle *_ICoreWebView2DocumentTitleChangedEventHandler 31 | callbackLoad *_ICoreWebView2SourceChangedEventHandler 32 | } 33 | 34 | func (r *driver) attach(w *webview) error { 35 | cerr := make(chan error, 1) 36 | 37 | if err := r.checkInstall(); err != nil { 38 | return err 39 | } 40 | 41 | // [Windows] Certs and Proxies must be defined before the initialization 42 | r.setCerts() 43 | r.setProxy() 44 | 45 | go r.config.RunOnMain(func() { 46 | windows.CoInitializeEx(0, 0x2) 47 | 48 | r.callbackLoad = &_ICoreWebView2SourceChangedEventHandler{ 49 | VTBL: _CoreWebView2SourceChangedEventHandlerVTBL, 50 | Invoke: func(this *_ICoreWebView2SourceChangedEventHandler, wv *_ICoreWebView2, v uintptr) uintptr { 51 | var url *uint16 52 | syscall.SyscallN(wv.VTBL.GetSource, uintptr(unsafe.Pointer(wv)), uintptr(unsafe.Pointer(&url))) 53 | 54 | w.fan.Send(NavigationEvent{ 55 | URL: windows.UTF16PtrToString(url), 56 | }) 57 | return 0 58 | }, 59 | } 60 | 61 | r.callbackTitle = &_ICoreWebView2DocumentTitleChangedEventHandler{ 62 | VTBL: _CoreWebView2DocumentTitleChangedEventHandlerVTBL, 63 | Invoke: func(this *_ICoreWebView2DocumentTitleChangedEventHandler, wv *_ICoreWebView2, v uintptr) uintptr { 64 | var title *uint16 65 | syscall.SyscallN(wv.VTBL.GetDocumentTitle, uintptr(unsafe.Pointer(wv)), uintptr(unsafe.Pointer(&title))) 66 | 67 | w.fan.Send(TitleEvent{ 68 | Title: windows.UTF16PtrToString(title), 69 | }) 70 | return 0 71 | }, 72 | } 73 | 74 | r.controllerCompletedHandler = &_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{ 75 | VTBL: _CoreWebView2CreateCoreWebView2ControllerCompletedHandlerVTBL, 76 | Invoke: func(this *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, err uintptr, val *_ICoreWebView2Controller) uintptr { 77 | r.controller = val 78 | syscall.SyscallN( 79 | val.VTBL._IUnknownVTBL.Add, 80 | uintptr(unsafe.Pointer(r.controller)), 81 | ) 82 | syscall.SyscallN( 83 | val.VTBL.GetCoreWebView2, 84 | uintptr(unsafe.Pointer(val)), 85 | uintptr(unsafe.Pointer(&r.webview2)), 86 | ) 87 | syscall.SyscallN(r.webview2.VTBL._IUnknownVTBL.Add, uintptr(unsafe.Pointer(r.webview2))) 88 | 89 | syscall.SyscallN(r.webview2.VTBL._IUnknownVTBL.Query, uintptr(unsafe.Pointer(r.webview2)), uintptr(unsafe.Pointer(&_GUIDCoreWebView22)), uintptr(unsafe.Pointer(&r.webview22))) 90 | 91 | // [Windows] Hook the events 92 | syscall.SyscallN(r.webview2.VTBL.AddSourceChanged, uintptr(unsafe.Pointer(r.webview2)), uintptr(unsafe.Pointer(r.callbackLoad))) 93 | syscall.SyscallN(r.webview2.VTBL.AddDocumentTitleChanged, uintptr(unsafe.Pointer(r.webview2)), uintptr(unsafe.Pointer(r.callbackTitle))) 94 | 95 | atomic.AddUint32(&r.active, 1) 96 | 97 | return 0 98 | }, 99 | } 100 | 101 | r.environmentCompletedHandler = &_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{ 102 | VTBL: _CoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVTBL, 103 | Invoke: func(this *_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, err uintptr, val *_ICoreWebView2Environment) uintptr { 104 | syscall.SyscallN(val.VTBL._IUnknownVTBL.Add, uintptr(unsafe.Pointer(val))) 105 | 106 | syscall.SyscallN(val.VTBL.CreateCoreWebView2Controller, uintptr(unsafe.Pointer(val)), r.config.HWND, uintptr(unsafe.Pointer(r.controllerCompletedHandler))) 107 | 108 | return 0 109 | }, 110 | } 111 | 112 | hr, _, _ := _CreateCoreWebView2EnvironmentWithOptions.Call(0, 0, 0, uint64(uintptr(unsafe.Pointer(r.environmentCompletedHandler)))) 113 | if hr != 0 { 114 | cerr <- ErrInvalidOptionChange 115 | return 116 | } 117 | 118 | cerr <- nil 119 | }) 120 | 121 | if err := <-cerr; err != nil { 122 | return err 123 | } 124 | 125 | go func() { 126 | for atomic.LoadUint32(&r.active) == 0 { 127 | } 128 | w.scheduler.SetRunner(r.config.RunOnMain) 129 | }() 130 | 131 | w.javascriptManager = newJavascriptManager(w) 132 | w.dataManager = newDataManager(w) 133 | 134 | return nil 135 | } 136 | 137 | func (r *driver) configure(w *webview, config Config) { 138 | r.config = config 139 | if atomic.LoadUint32(&r.active) == 1 { 140 | w.scheduler.SetRunner(w.driver.config.RunOnMain) 141 | } 142 | } 143 | 144 | func (r *driver) resize(w *webview, pos [4]float32) { 145 | if pos[2] == 0 && pos[3] == 0 { 146 | syscall.SyscallN( 147 | r.controller.VTBL.PutIsVisible, 148 | uintptr(unsafe.Pointer(r.controller)), 149 | 0, 150 | ) 151 | } else { 152 | pos := [4]int32{int32(pos[0] + 0.5), int32(pos[1] + 0.5), int32(pos[0]+0.5) + int32(pos[2]+0.5), int32(pos[1]+0.5) + int32(pos[3]+0.5)} 153 | syscall.SyscallN( 154 | r.controller.VTBL.PutIsVisible, 155 | uintptr(unsafe.Pointer(r.controller)), 156 | 1, 157 | ) 158 | syscall.SyscallN( 159 | r.controller.VTBL.PutBounds, 160 | uintptr(unsafe.Pointer(r.controller)), 161 | uintptr(unsafe.Pointer(&pos)), 162 | ) 163 | } 164 | } 165 | 166 | func (r *driver) navigate(w *webview, url *url.URL) { 167 | syscall.SyscallN( 168 | r.webview2.VTBL.Navigate, 169 | uintptr(unsafe.Pointer(r.webview2)), 170 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(url.String()))), 171 | 0, 172 | ) 173 | } 174 | 175 | func (r *driver) close(w *webview) { 176 | if r.webview2 != nil { 177 | syscall.SyscallN( 178 | r.webview2.VTBL._IUnknownVTBL.Release, 179 | uintptr(unsafe.Pointer(r.webview2)), 180 | ) 181 | } 182 | if r.controller != nil { 183 | syscall.SyscallN( 184 | r.controller.VTBL._IUnknownVTBL.Release, 185 | uintptr(unsafe.Pointer(r.controller)), 186 | ) 187 | } 188 | } 189 | 190 | func (r *driver) checkInstall() error { 191 | haveInstalled := false 192 | 193 | for _, local := range [...]registry.Key{registry.LOCAL_MACHINE, registry.CURRENT_USER} { 194 | for _, p := range registryPaths { 195 | key, err := registry.OpenKey(local, p, registry.QUERY_VALUE) 196 | if err != nil { 197 | continue 198 | } 199 | 200 | version, _, err := key.GetStringValue(`pv`) 201 | if err != nil || version == "" { 202 | continue 203 | } 204 | 205 | haveInstalled = true 206 | break 207 | } 208 | } 209 | 210 | if !haveInstalled { 211 | return ErrNotInstalled 212 | } 213 | return nil 214 | } 215 | --------------------------------------------------------------------------------