fix: add involves edge from task to agent:nomos at creation
Plus sync vendor directory for Docker build compatibility.
This commit is contained in:
161
vendor/github.com/wailsapp/wails/v3/internal/assetserver/asset_fileserver.go
generated
vendored
Normal file
161
vendor/github.com/wailsapp/wails/v3/internal/assetserver/asset_fileserver.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
indexHTML = "index.html"
|
||||
)
|
||||
|
||||
type assetFileServer struct {
|
||||
fs iofs.FS
|
||||
err error
|
||||
}
|
||||
|
||||
func newAssetFileServerFS(vfs iofs.FS) http.Handler {
|
||||
subDir, err := findPathToFile(vfs, indexHTML)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
msg := "no `index.html` could be found in your Assets fs.FS"
|
||||
if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs {
|
||||
rootFolder, _ := findEmbedRootPath(embedFs)
|
||||
msg += fmt.Sprintf(", please make sure the embedded directory '%s' is correct and contains your assets", rootFolder)
|
||||
}
|
||||
|
||||
err = errors.New(msg)
|
||||
}
|
||||
} else {
|
||||
vfs, err = iofs.Sub(vfs, path.Clean(subDir))
|
||||
}
|
||||
|
||||
return &assetFileServer{fs: vfs, err: err}
|
||||
}
|
||||
|
||||
func (d *assetFileServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
url := req.URL.Path
|
||||
|
||||
err := d.err
|
||||
if err == nil {
|
||||
filename := path.Clean(strings.TrimPrefix(url, "/"))
|
||||
d.logInfo(ctx, "Handling request", "url", url, "file", filename)
|
||||
err = d.serveFSFile(rw, req, filename)
|
||||
if os.IsNotExist(err) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
d.logError(ctx, "Unable to handle request", "url", url, "err", err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// serveFile will try to load the file from the fs.FS and write it to the response
|
||||
func (d *assetFileServer) serveFSFile(rw http.ResponseWriter, req *http.Request, filename string) error {
|
||||
if d.fs == nil {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
file, err := d.fs.Open(filename)
|
||||
if err != nil {
|
||||
if s := path.Ext(filename); s == "" {
|
||||
filename = filename + ".html"
|
||||
file, err = d.fs.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
statInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := req.URL.Path
|
||||
isDirectoryPath := url == "" || url[len(url)-1] == '/'
|
||||
if statInfo.IsDir() {
|
||||
if !isDirectoryPath {
|
||||
// If the URL doesn't end in a slash normally a http.redirect should be done, but that currently doesn't work on
|
||||
// WebKit WebViews (macOS/Linux).
|
||||
// So we handle this as a specific error
|
||||
return fmt.Errorf("a directory has been requested without a trailing slash, please add a trailing slash to your request")
|
||||
}
|
||||
|
||||
filename = path.Join(filename, indexHTML)
|
||||
|
||||
file, err = d.fs.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
statInfo, err = file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if isDirectoryPath {
|
||||
return fmt.Errorf("a file has been requested with a trailing slash, please remove the trailing slash from your request")
|
||||
}
|
||||
|
||||
var buf [512]byte
|
||||
var n int
|
||||
if _, haveType := rw.Header()[HeaderContentType]; !haveType {
|
||||
// Detect MimeType by sniffing the first 512 bytes
|
||||
n, err = file.Read(buf[:])
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do the custom MimeType sniffing even though http.ServeContent would do it in case
|
||||
// of an io.ReadSeeker. We would like to have a consistent behaviour in both cases.
|
||||
if contentType := GetMimetype(filename, buf[:n]); contentType != "" {
|
||||
rw.Header().Set(HeaderContentType, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
if fileSeeker, _ := file.(io.ReadSeeker); fileSeeker != nil {
|
||||
if _, err := fileSeeker.Seek(0, io.SeekStart); err != nil {
|
||||
return fmt.Errorf("seeker can't seek")
|
||||
}
|
||||
|
||||
http.ServeContent(rw, req, statInfo.Name(), statInfo.ModTime(), fileSeeker)
|
||||
return nil
|
||||
}
|
||||
|
||||
rw.Header().Set(HeaderContentLength, fmt.Sprintf("%d", statInfo.Size()))
|
||||
|
||||
// Write the first 512 bytes used for MimeType sniffing
|
||||
_, err = io.Copy(rw, bytes.NewReader(buf[:n]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the remaining content of the file
|
||||
_, err = io.Copy(rw, file)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *assetFileServer) logInfo(ctx context.Context, message string, args ...interface{}) {
|
||||
logInfo(ctx, "[AssetFileServerFS] "+message, args...)
|
||||
}
|
||||
|
||||
func (d *assetFileServer) logError(ctx context.Context, message string, args ...interface{}) {
|
||||
logError(ctx, "[AssetFileServerFS] "+message, args...)
|
||||
}
|
||||
175
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver.go
generated
vendored
Normal file
175
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
webViewRequestHeaderWindowId = "x-wails-window-id"
|
||||
webViewRequestHeaderWindowName = "x-wails-window-name"
|
||||
HeaderAcceptLanguage = "accept-language"
|
||||
)
|
||||
|
||||
type RuntimeHandler interface {
|
||||
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
Route string
|
||||
Handler http.Handler
|
||||
}
|
||||
|
||||
type AssetServer struct {
|
||||
options *Options
|
||||
handler http.Handler
|
||||
services []service
|
||||
|
||||
assetServerWebView
|
||||
}
|
||||
|
||||
func NewAssetServer(options *Options) (*AssetServer, error) {
|
||||
result := &AssetServer{
|
||||
options: options,
|
||||
}
|
||||
|
||||
userHandler := options.Handler
|
||||
if userHandler == nil {
|
||||
userHandler = http.NotFoundHandler()
|
||||
}
|
||||
|
||||
handler := http.Handler(
|
||||
http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
result.serveHTTP(w, r, userHandler)
|
||||
}))
|
||||
|
||||
if middleware := options.Middleware; middleware != nil {
|
||||
handler = middleware(handler)
|
||||
}
|
||||
|
||||
result.handler = handler
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
start := time.Now()
|
||||
wrapped := newContentTypeSniffer(rw)
|
||||
defer func() {
|
||||
if _, err := wrapped.complete(); err != nil {
|
||||
a.options.Logger.Error("Error writing response data.", "uri", req.RequestURI, "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
req = req.WithContext(contextWithLogger(req.Context(), a.options.Logger))
|
||||
a.handler.ServeHTTP(wrapped, req)
|
||||
|
||||
a.options.Logger.Debug(
|
||||
"Asset Request:",
|
||||
"windowName", req.Header.Get(webViewRequestHeaderWindowName),
|
||||
"windowID", req.Header.Get(webViewRequestHeaderWindowId),
|
||||
"code", wrapped.status,
|
||||
"method", req.Method,
|
||||
"path", req.URL.EscapedPath(),
|
||||
"duration", time.Since(start),
|
||||
)
|
||||
}
|
||||
|
||||
func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userHandler http.Handler) {
|
||||
if isWebSocket(req) {
|
||||
// WebSockets are not supported by the AssetServer
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
header := rw.Header()
|
||||
// TODO: I don't think this is needed now?
|
||||
//if a.servingFromDisk {
|
||||
// header.Add(HeaderCacheControl, "no-cache")
|
||||
//}
|
||||
|
||||
reqPath := req.URL.Path
|
||||
switch reqPath {
|
||||
case "", "/", "/index.html":
|
||||
// Cache the accept-language header
|
||||
// before passing the request down the chain.
|
||||
acceptLanguage := req.Header.Get(HeaderAcceptLanguage)
|
||||
if acceptLanguage == "" {
|
||||
acceptLanguage = "en"
|
||||
}
|
||||
|
||||
wrapped := &fallbackResponseWriter{
|
||||
rw: rw,
|
||||
req: req,
|
||||
fallback: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
// Set content type for default index.html
|
||||
header.Set(HeaderContentType, "text/html; charset=utf-8")
|
||||
a.writeBlob(rw, indexHTML, defaultIndexHTML(acceptLanguage))
|
||||
}),
|
||||
}
|
||||
userHandler.ServeHTTP(wrapped, req)
|
||||
|
||||
default:
|
||||
// Check if the path matches a service route
|
||||
for _, svc := range a.services {
|
||||
if strings.HasPrefix(reqPath, svc.Route) {
|
||||
req.URL.Path = strings.TrimPrefix(reqPath, svc.Route)
|
||||
svc.Handler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Forward to the user-provided handler
|
||||
userHandler.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AssetServer) AttachServiceHandler(route string, handler http.Handler) {
|
||||
a.services = append(a.services, service{route, handler})
|
||||
}
|
||||
|
||||
func (a *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) {
|
||||
err := ServeFile(rw, filename, blob)
|
||||
if err != nil {
|
||||
a.serveError(rw, err, "Error writing file content.", "filename", filename)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, args ...interface{}) {
|
||||
args = append(args, "error", err)
|
||||
a.options.Logger.Error(msg, args...)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func GetStartURL(userURL string) (string, error) {
|
||||
devServerURL := GetDevServerURL()
|
||||
startURL := baseURL.String()
|
||||
if devServerURL != "" {
|
||||
// Parse the port
|
||||
parsedURL, err := url.Parse(devServerURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing environment variable `FRONTEND_DEVSERVER_URL`: %w. Please check your `Taskfile.yml` file", err)
|
||||
}
|
||||
port := parsedURL.Port()
|
||||
if port != "" {
|
||||
baseURL.Host = net.JoinHostPort(baseURL.Hostname(), port)
|
||||
startURL = baseURL.String()
|
||||
}
|
||||
}
|
||||
|
||||
if userURL != "" {
|
||||
parsedURL, err := baseURL.Parse(userURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing URL: %w", err)
|
||||
}
|
||||
|
||||
startURL = parsedURL.String()
|
||||
}
|
||||
|
||||
return startURL, nil
|
||||
}
|
||||
12
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_android.go
generated
vendored
Normal file
12
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_android.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build android
|
||||
|
||||
package assetserver
|
||||
|
||||
import "net/url"
|
||||
|
||||
// Android uses https://wails.localhost as the base URL
|
||||
// This matches the WebViewAssetLoader domain configuration
|
||||
var baseURL = url.URL{
|
||||
Scheme: "https",
|
||||
Host: "wails.localhost",
|
||||
}
|
||||
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_darwin.go
generated
vendored
Normal file
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build darwin && !ios
|
||||
|
||||
package assetserver
|
||||
|
||||
import "net/url"
|
||||
|
||||
var baseURL = url.URL{
|
||||
Scheme: "wails",
|
||||
Host: "localhost",
|
||||
}
|
||||
50
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_dev.go
generated
vendored
Normal file
50
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_dev.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
//go:build !production
|
||||
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
)
|
||||
|
||||
//go:embed defaults
|
||||
var defaultHTML embed.FS
|
||||
|
||||
func defaultIndexHTML(language string) []byte {
|
||||
result := []byte("index.html not found")
|
||||
// Create an fs.Sub in the defaults directory
|
||||
defaults, err := iofs.Sub(defaultHTML, "defaults")
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
// Get the 2 character language code
|
||||
lang := "en"
|
||||
if len(language) >= 2 {
|
||||
lang = language[:2]
|
||||
}
|
||||
// Now we can read the index.html file in the format
|
||||
// index.<lang>.html.
|
||||
|
||||
indexFile, err := defaults.Open("index." + lang + ".html")
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
indexBytes, err := io.ReadAll(indexFile)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
return indexBytes
|
||||
}
|
||||
|
||||
func (a *AssetServer) LogDetails() {
|
||||
var info = []any{
|
||||
"middleware", a.options.Middleware != nil,
|
||||
"handler", a.options.Handler != nil,
|
||||
}
|
||||
if devServerURL := GetDevServerURL(); devServerURL != "" {
|
||||
info = append(info, "devServerURL", devServerURL)
|
||||
}
|
||||
a.options.Logger.Info("AssetServer Info:", info...)
|
||||
}
|
||||
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_ios.go
generated
vendored
Normal file
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_ios.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build ios
|
||||
|
||||
package assetserver
|
||||
|
||||
import "net/url"
|
||||
|
||||
var baseURL = url.URL{
|
||||
Scheme: "wails",
|
||||
Host: "localhost",
|
||||
}
|
||||
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_linux.go
generated
vendored
Normal file
10
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_linux.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package assetserver
|
||||
|
||||
import "net/url"
|
||||
|
||||
var baseURL = url.URL{
|
||||
Scheme: "wails",
|
||||
Host: "localhost",
|
||||
}
|
||||
9
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_production.go
generated
vendored
Normal file
9
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_production.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build production
|
||||
|
||||
package assetserver
|
||||
|
||||
func defaultIndexHTML(_ string) []byte {
|
||||
return []byte("index.html not found")
|
||||
}
|
||||
|
||||
func (a *AssetServer) LogDetails() {}
|
||||
198
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_webview.go
generated
vendored
Normal file
198
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_webview.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v3/internal/assetserver/webview"
|
||||
)
|
||||
|
||||
type assetServerWebView struct {
|
||||
// ExpectedWebViewHost is checked against the Request Host of every WebViewRequest, other hosts won't be processed.
|
||||
ExpectedWebViewHost string
|
||||
|
||||
dispatchInit sync.Once
|
||||
dispatchReqC chan<- webview.Request
|
||||
dispatchWorkers int
|
||||
}
|
||||
|
||||
// ServeWebViewRequest processes the HTTP Request asynchronously by faking a golang HTTP Server.
|
||||
// The request will be finished with a StatusNotImplemented code if no handler has written to the response.
|
||||
// The AssetServer takes ownership of the request and the caller mustn't close it or access it in any other way.
|
||||
func (a *AssetServer) ServeWebViewRequest(req webview.Request) {
|
||||
a.dispatchInit.Do(func() {
|
||||
workers := a.dispatchWorkers
|
||||
if workers <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
workerC := make(chan webview.Request, workers*2)
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
for req := range workerC {
|
||||
a.processWebViewRequest(req)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
dispatchC := make(chan webview.Request)
|
||||
go queueingDispatcher(50, dispatchC, workerC)
|
||||
|
||||
a.dispatchReqC = dispatchC
|
||||
})
|
||||
|
||||
if a.dispatchReqC == nil {
|
||||
go a.processWebViewRequest(req)
|
||||
} else {
|
||||
a.dispatchReqC <- req
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AssetServer) processWebViewRequest(r webview.Request) {
|
||||
uri, _ := r.URL()
|
||||
a.processWebViewRequestInternal(r)
|
||||
if err := r.Close(); err != nil {
|
||||
a.options.Logger.Error("Unable to call close for request for uri.", "uri", uri)
|
||||
}
|
||||
}
|
||||
|
||||
// processHTTPRequest processes the HTTP Request by faking a golang HTTP Server.
|
||||
// The request will be finished with a StatusNotImplemented code if no handler has written to the response.
|
||||
func (a *AssetServer) processWebViewRequestInternal(r webview.Request) {
|
||||
uri := "unknown"
|
||||
var err error
|
||||
|
||||
wrw := r.Response()
|
||||
defer func() {
|
||||
if err := wrw.Finish(); err != nil {
|
||||
a.options.Logger.Error("Error finishing request.", "uri", uri, "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
rw := newContentTypeSniffer(wrw) // Make sure we have a Content-Type sniffer
|
||||
defer func() {
|
||||
if _, err := rw.complete(); err != nil {
|
||||
a.options.Logger.Error("Error writing response data.", "uri", uri, "error", err)
|
||||
}
|
||||
}()
|
||||
defer rw.WriteHeader(http.StatusNotImplemented) // This is a NOP when a handler has already written and set the status
|
||||
|
||||
uri, err = r.URL()
|
||||
if err != nil {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("URL: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
method, err := r.Method()
|
||||
if err != nil {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Method: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
header, err := r.Header()
|
||||
if err != nil {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Header: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := r.Body()
|
||||
if err != nil {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Body: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
if body == nil {
|
||||
body = http.NoBody
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, uri, body)
|
||||
if err != nil {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("HTTP-Request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// For server requests, the URL is parsed from the URI supplied on the Request-Line as stored in RequestURI. For
|
||||
// most requests, fields other than Path and RawQuery will be empty. (See RFC 7230, Section 5.3)
|
||||
req.URL.Scheme = ""
|
||||
req.URL.Host = ""
|
||||
req.URL.Fragment = ""
|
||||
req.URL.RawFragment = ""
|
||||
|
||||
if requestURL := req.URL; req.RequestURI == "" && requestURL != nil {
|
||||
req.RequestURI = requestURL.String()
|
||||
}
|
||||
|
||||
req.Header = header
|
||||
|
||||
if req.RemoteAddr == "" {
|
||||
// 192.0.2.0/24 is "TEST-NET" in RFC 5737
|
||||
req.RemoteAddr = "192.0.2.1:1234"
|
||||
}
|
||||
|
||||
if req.RequestURI == "" && req.URL != nil {
|
||||
req.RequestURI = req.URL.String()
|
||||
}
|
||||
|
||||
if req.ContentLength == 0 {
|
||||
req.ContentLength, _ = strconv.ParseInt(req.Header.Get(HeaderContentLength), 10, 64)
|
||||
} else {
|
||||
req.Header.Set(HeaderContentLength, fmt.Sprintf("%d", req.ContentLength))
|
||||
}
|
||||
|
||||
if host := req.Header.Get(HeaderHost); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
// iOS uses "localhost" while other platforms might use different hosts
|
||||
// Skip host check for iOS requests from wails:// scheme
|
||||
if expectedHost := a.ExpectedWebViewHost; expectedHost != "" && expectedHost != req.Host && !strings.HasPrefix(uri, "wails://") {
|
||||
a.webviewRequestErrorHandler(uri, rw, fmt.Errorf("expected host '%s' in request, but was '%s'", expectedHost, req.Host))
|
||||
return
|
||||
}
|
||||
|
||||
a.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func (a *AssetServer) webviewRequestErrorHandler(uri string, rw http.ResponseWriter, err error) {
|
||||
logInfo := uri
|
||||
if uri, err := url.ParseRequestURI(uri); err == nil {
|
||||
logInfo = strings.Replace(logInfo, fmt.Sprintf("%s://%s", uri.Scheme, uri.Host), "", 1)
|
||||
}
|
||||
|
||||
a.options.Logger.Error("Error processing request (HttpResponse=500)", "details", logInfo, "error", err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func queueingDispatcher[T any](minQueueSize uint, inC <-chan T, outC chan<- T) {
|
||||
q := newRingqueue[T](minQueueSize)
|
||||
for {
|
||||
in, ok := <-inC
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
q.Add(in)
|
||||
for q.Len() != 0 {
|
||||
out, _ := q.Peek()
|
||||
select {
|
||||
case outC <- out:
|
||||
q.Remove()
|
||||
case in, ok := <-inC:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
q.Add(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_windows.go
generated
vendored
Normal file
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/assetserver_windows.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package assetserver
|
||||
|
||||
import "net/url"
|
||||
|
||||
var baseURL = url.URL{
|
||||
Scheme: "http",
|
||||
Host: "wails.localhost",
|
||||
}
|
||||
102
vendor/github.com/wailsapp/wails/v3/internal/assetserver/build_dev.go
generated
vendored
Normal file
102
vendor/github.com/wailsapp/wails/v3/internal/assetserver/build_dev.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//go:build !production
|
||||
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// retryTransport implements http.RoundTripper with retry logic for transient connection failures.
|
||||
// This is particularly useful when the Vite dev server temporarily rejects connections due to
|
||||
// high concurrency with many dynamic imports.
|
||||
type retryTransport struct {
|
||||
base http.RoundTripper
|
||||
maxRetries int
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
// RoundTrip executes a single HTTP transaction with retry logic.
|
||||
func (t *retryTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
var resp *http.Response
|
||||
var err error
|
||||
for i := 0; i < t.maxRetries; i++ {
|
||||
resp, err = t.base.RoundTrip(req)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
// Only retry on connection errors (e.g., connection refused)
|
||||
if isConnectionError(err) && i < t.maxRetries-1 {
|
||||
time.Sleep(t.delay)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// isConnectionError checks if the error is a connection-related error that may be transient.
|
||||
func isConnectionError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errStr := strings.ToLower(err.Error())
|
||||
return strings.Contains(errStr, "connection refused") ||
|
||||
strings.Contains(errStr, "connection reset") ||
|
||||
strings.Contains(errStr, "broken pipe") ||
|
||||
strings.Contains(errStr, "connectex")
|
||||
}
|
||||
|
||||
func NewAssetFileServer(vfs fs.FS) http.Handler {
|
||||
devServerURL := GetDevServerURL()
|
||||
if devServerURL == "" {
|
||||
return newAssetFileServerFS(vfs)
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(devServerURL)
|
||||
if err != nil {
|
||||
return http.HandlerFunc(
|
||||
func(rw http.ResponseWriter, req *http.Request) {
|
||||
logError(req.Context(), "[ExternalAssetHandler] Invalid FRONTEND_DEVSERVER_URL. Should be valid URL", "error", err.Error())
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(parsedURL)
|
||||
proxy.Transport = &retryTransport{
|
||||
base: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// Force IPv4 for localhost connections to avoid IPv6 issues on Windows
|
||||
if parsedURL.Hostname() == "localhost" || parsedURL.Hostname() == "127.0.0.1" {
|
||||
return dialer.DialContext(ctx, "tcp4", addr)
|
||||
}
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
},
|
||||
},
|
||||
maxRetries: 50,
|
||||
delay: 50 * time.Millisecond,
|
||||
}
|
||||
proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) {
|
||||
logError(r.Context(), "[ExternalAssetHandler] Proxy error", "error", err.Error())
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
}
|
||||
|
||||
return proxy
|
||||
}
|
||||
|
||||
func GetDevServerURL() string {
|
||||
return os.Getenv("FRONTEND_DEVSERVER_URL")
|
||||
}
|
||||
16
vendor/github.com/wailsapp/wails/v3/internal/assetserver/build_production.go
generated
vendored
Normal file
16
vendor/github.com/wailsapp/wails/v3/internal/assetserver/build_production.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build production
|
||||
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewAssetFileServer(vfs fs.FS) http.Handler {
|
||||
return newAssetFileServerFS(vfs)
|
||||
}
|
||||
|
||||
func GetDevServerURL() string {
|
||||
return ""
|
||||
}
|
||||
33
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundled_assetserver.go
generated
vendored
Normal file
33
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundled_assetserver.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/internal/assetserver/bundledassets"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BundledAssetServer struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func NewBundledAssetFileServer(fs fs.FS) *BundledAssetServer {
|
||||
return &BundledAssetServer{
|
||||
handler: NewAssetFileServer(fs),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BundledAssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if strings.HasPrefix(req.URL.Path, "/wails/") {
|
||||
// Strip the /wails prefix
|
||||
req.URL.Path = req.URL.Path[6:]
|
||||
switch req.URL.Path {
|
||||
case "/runtime.js":
|
||||
rw.Header().Set("Content-Type", "application/javascript")
|
||||
rw.Write([]byte(bundledassets.RuntimeJS))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
b.handler.ServeHTTP(rw, req)
|
||||
}
|
||||
3245
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime.debug.js
generated
vendored
Normal file
3245
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime.debug.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime.js
generated
vendored
Normal file
1
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime_dev.go
generated
vendored
Normal file
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime_dev.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build !production
|
||||
|
||||
package bundledassets
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed runtime.debug.js
|
||||
var RuntimeJS []byte
|
||||
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime_production.go
generated
vendored
Normal file
8
vendor/github.com/wailsapp/wails/v3/internal/assetserver/bundledassets/runtime_production.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build production
|
||||
|
||||
package bundledassets
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed runtime.js
|
||||
var RuntimeJS []byte
|
||||
66
vendor/github.com/wailsapp/wails/v3/internal/assetserver/common.go
generated
vendored
Normal file
66
vendor/github.com/wailsapp/wails/v3/internal/assetserver/common.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderHost = "Host"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
// TODO: Is this needed?
|
||||
HeaderCacheControl = "Cache-Control"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
|
||||
WailsUserAgentValue = "wails.io"
|
||||
)
|
||||
|
||||
type assetServerLogger struct{}
|
||||
|
||||
var assetServerLoggerKey assetServerLogger
|
||||
|
||||
// ServeFile writes the provided blob to rw as an HTTP 200 response, ensuring appropriate
|
||||
// Content-Length and Content-Type headers are set.
|
||||
//
|
||||
// If the Content-Type header is not already present, ServeFile determines an appropriate
|
||||
// MIME type from the filename and blob and sets the Content-Type header. It then writes
|
||||
// the 200 status and the blob body to the response, returning any error encountered while
|
||||
// writing the body.
|
||||
func ServeFile(rw http.ResponseWriter, filename string, blob []byte) error {
|
||||
header := rw.Header()
|
||||
header.Set(HeaderContentLength, fmt.Sprintf("%d", len(blob)))
|
||||
if mimeType := header.Get(HeaderContentType); mimeType == "" {
|
||||
mimeType = GetMimetype(filename, blob)
|
||||
header.Set(HeaderContentType, mimeType)
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err := rw.Write(blob)
|
||||
return err
|
||||
}
|
||||
|
||||
func isWebSocket(req *http.Request) bool {
|
||||
upgrade := req.Header.Get(HeaderUpgrade)
|
||||
return strings.EqualFold(upgrade, "websocket")
|
||||
}
|
||||
|
||||
func contextWithLogger(ctx context.Context, logger *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, assetServerLoggerKey, logger)
|
||||
}
|
||||
|
||||
func logInfo(ctx context.Context, message string, args ...interface{}) {
|
||||
if logger, _ := ctx.Value(assetServerLoggerKey).(*slog.Logger); logger != nil {
|
||||
logger.Info(message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func logError(ctx context.Context, message string, args ...interface{}) {
|
||||
if logger, _ := ctx.Value(assetServerLoggerKey).(*slog.Logger); logger != nil {
|
||||
logger.Error(message, args...)
|
||||
}
|
||||
}
|
||||
142
vendor/github.com/wailsapp/wails/v3/internal/assetserver/content_type_sniffer.go
generated
vendored
Normal file
142
vendor/github.com/wailsapp/wails/v3/internal/assetserver/content_type_sniffer.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// newContentTypeSniffer creates a contentTypeSniffer that wraps the provided http.ResponseWriter.
|
||||
// The returned sniffer does not allocate a close notification channel; it will be initialized lazily by CloseNotify.
|
||||
func newContentTypeSniffer(rw http.ResponseWriter) *contentTypeSniffer {
|
||||
return &contentTypeSniffer{
|
||||
rw: rw,
|
||||
}
|
||||
}
|
||||
|
||||
type contentTypeSniffer struct {
|
||||
rw http.ResponseWriter
|
||||
prefix []byte
|
||||
closeChannel chan bool // lazily allocated only if CloseNotify is called
|
||||
status int
|
||||
headerCommitted bool
|
||||
headerWritten bool
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped [http.ResponseWriter] for use with [http.ResponseController].
|
||||
func (rw *contentTypeSniffer) Unwrap() http.ResponseWriter {
|
||||
return rw.rw
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) Header() http.Header {
|
||||
return rw.rw.Header()
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) Write(chunk []byte) (int, error) {
|
||||
if !rw.headerCommitted {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
if rw.headerWritten {
|
||||
return rw.rw.Write(chunk)
|
||||
}
|
||||
|
||||
if len(chunk) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Cut away at most 512 bytes from chunk, and not less than 0.
|
||||
cut := max(min(len(chunk), 512-len(rw.prefix)), 0)
|
||||
if cut >= 512 {
|
||||
// Avoid copying data if a full prefix is available on first non-zero write.
|
||||
cut = len(chunk)
|
||||
rw.prefix = chunk
|
||||
chunk = nil
|
||||
} else if cut > 0 {
|
||||
// First write had less than 512 bytes -- copy data to the prefix buffer.
|
||||
if rw.prefix == nil {
|
||||
// Preallocate space for the prefix to be used for sniffing.
|
||||
rw.prefix = make([]byte, 0, 512)
|
||||
}
|
||||
rw.prefix = append(rw.prefix, chunk[:cut]...)
|
||||
chunk = chunk[cut:]
|
||||
}
|
||||
|
||||
if len(rw.prefix) < 512 {
|
||||
return cut, nil
|
||||
}
|
||||
|
||||
if _, err := rw.complete(); err != nil {
|
||||
return cut, err
|
||||
}
|
||||
|
||||
n, err := rw.rw.Write(chunk)
|
||||
return cut + n, err
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) WriteHeader(code int) {
|
||||
if rw.headerCommitted {
|
||||
return
|
||||
}
|
||||
|
||||
rw.status = code
|
||||
rw.headerCommitted = true
|
||||
|
||||
if _, hasType := rw.Header()[HeaderContentType]; hasType {
|
||||
rw.rw.WriteHeader(rw.status)
|
||||
rw.headerWritten = true
|
||||
}
|
||||
}
|
||||
|
||||
// sniff sniffs the content type from the stored prefix if necessary,
|
||||
// then writes the header.
|
||||
func (rw *contentTypeSniffer) sniff() {
|
||||
if rw.headerWritten || !rw.headerCommitted {
|
||||
return
|
||||
}
|
||||
|
||||
m := rw.Header()
|
||||
if _, hasType := m[HeaderContentType]; !hasType {
|
||||
m.Set(HeaderContentType, http.DetectContentType(rw.prefix))
|
||||
}
|
||||
|
||||
rw.rw.WriteHeader(rw.status)
|
||||
rw.headerWritten = true
|
||||
}
|
||||
|
||||
// complete sniffs the content type if necessary, writes the header
|
||||
// and sends the data prefix that has been stored for sniffing.
|
||||
//
|
||||
// Whoever creates a contentTypeSniffer instance
|
||||
// is responsible for calling complete after the nested handler has returned.
|
||||
func (rw *contentTypeSniffer) complete() (n int, err error) {
|
||||
rw.sniff()
|
||||
|
||||
if rw.headerWritten && len(rw.prefix) > 0 {
|
||||
n, err = rw.rw.Write(rw.prefix)
|
||||
rw.prefix = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CloseNotify implements the http.CloseNotifier interface.
|
||||
// The channel is lazily allocated to avoid allocation overhead for requests
|
||||
// that don't use this deprecated interface.
|
||||
func (rw *contentTypeSniffer) CloseNotify() <-chan bool {
|
||||
if rw.closeChannel == nil {
|
||||
rw.closeChannel = make(chan bool, 1)
|
||||
}
|
||||
return rw.closeChannel
|
||||
}
|
||||
|
||||
func (rw *contentTypeSniffer) closeClient() {
|
||||
if rw.closeChannel != nil {
|
||||
rw.closeChannel <- true
|
||||
}
|
||||
}
|
||||
|
||||
// Flush implements the http.Flusher interface.
|
||||
func (rw *contentTypeSniffer) Flush() {
|
||||
if f, ok := rw.rw.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
350
vendor/github.com/wailsapp/wails/v3/internal/assetserver/defaults/index.en.html
generated
vendored
Normal file
350
vendor/github.com/wailsapp/wails/v3/internal/assetserver/defaults/index.en.html
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
302
vendor/github.com/wailsapp/wails/v3/internal/assetserver/defaults/index.zh.html
generated
vendored
Normal file
302
vendor/github.com/wailsapp/wails/v3/internal/assetserver/defaults/index.zh.html
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
80
vendor/github.com/wailsapp/wails/v3/internal/assetserver/fallback_response_writer.go
generated
vendored
Normal file
80
vendor/github.com/wailsapp/wails/v3/internal/assetserver/fallback_response_writer.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// fallbackResponseWriter wraps a [http.ResponseWriter].
|
||||
// If the main handler returns status code 404,
|
||||
// its response is discarded
|
||||
// and the request is forwarded to the fallback handler.
|
||||
type fallbackResponseWriter struct {
|
||||
rw http.ResponseWriter
|
||||
req *http.Request
|
||||
fallback http.Handler
|
||||
|
||||
header http.Header
|
||||
headerWritten bool
|
||||
complete bool
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped [http.ResponseWriter] for use with [http.ResponseController].
|
||||
func (fw *fallbackResponseWriter) Unwrap() http.ResponseWriter {
|
||||
return fw.rw
|
||||
}
|
||||
|
||||
func (fw *fallbackResponseWriter) Header() http.Header {
|
||||
if fw.header == nil {
|
||||
// Preserve original header in case we get a 404 response.
|
||||
fw.header = fw.rw.Header().Clone()
|
||||
}
|
||||
return fw.header
|
||||
}
|
||||
|
||||
func (fw *fallbackResponseWriter) Write(chunk []byte) (int, error) {
|
||||
if fw.complete {
|
||||
// Fallback triggered, discard further writes.
|
||||
return len(chunk), nil
|
||||
}
|
||||
|
||||
if !fw.headerWritten {
|
||||
fw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
return fw.rw.Write(chunk)
|
||||
}
|
||||
|
||||
func (fw *fallbackResponseWriter) WriteHeader(statusCode int) {
|
||||
if fw.headerWritten {
|
||||
return
|
||||
}
|
||||
fw.headerWritten = true
|
||||
|
||||
if statusCode == http.StatusNotFound {
|
||||
// Protect fallback header from external modifications.
|
||||
if fw.header == nil {
|
||||
fw.header = fw.rw.Header().Clone()
|
||||
}
|
||||
|
||||
// Invoke fallback handler.
|
||||
fw.complete = true
|
||||
fw.fallback.ServeHTTP(fw.rw, fw.req)
|
||||
return
|
||||
}
|
||||
|
||||
if fw.header != nil {
|
||||
// Apply headers and forward original map to the main handler.
|
||||
maps.Copy(fw.rw.Header(), fw.header)
|
||||
fw.header = fw.rw.Header()
|
||||
}
|
||||
|
||||
fw.rw.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
// Flush implements the http.Flusher interface.
|
||||
func (rw *fallbackResponseWriter) Flush() {
|
||||
if f, ok := rw.rw.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
76
vendor/github.com/wailsapp/wails/v3/internal/assetserver/fs.go
generated
vendored
Normal file
76
vendor/github.com/wailsapp/wails/v3/internal/assetserver/fs.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// findEmbedRootPath finds the root path in the embed FS. It's the directory which contains all the files.
|
||||
func findEmbedRootPath(fileSystem embed.FS) (string, error) {
|
||||
stopErr := errors.New("files or multiple dirs found")
|
||||
|
||||
fPath := ""
|
||||
err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
fPath = path
|
||||
if entries, dErr := fs.ReadDir(fileSystem, path); dErr != nil {
|
||||
return dErr
|
||||
} else if len(entries) <= 1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return stopErr
|
||||
})
|
||||
|
||||
if err != nil && !errors.Is(err, stopErr) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fPath, nil
|
||||
}
|
||||
|
||||
func findPathToFile(fileSystem fs.FS, file string) (string, error) {
|
||||
stat, _ := fs.Stat(fileSystem, file)
|
||||
if stat != nil {
|
||||
return ".", nil
|
||||
}
|
||||
var indexFiles []string
|
||||
err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(path, file) {
|
||||
indexFiles = append(indexFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(indexFiles) > 1 {
|
||||
selected := indexFiles[0]
|
||||
for _, f := range indexFiles {
|
||||
if len(f) < len(selected) {
|
||||
selected = f
|
||||
}
|
||||
}
|
||||
path, _ := filepath.Split(selected)
|
||||
return path, nil
|
||||
}
|
||||
if len(indexFiles) > 0 {
|
||||
path, _ := filepath.Split(indexFiles[0])
|
||||
return path, nil
|
||||
}
|
||||
return "", fmt.Errorf("%s: %w", file, os.ErrNotExist)
|
||||
}
|
||||
20
vendor/github.com/wailsapp/wails/v3/internal/assetserver/middleware.go
generated
vendored
Normal file
20
vendor/github.com/wailsapp/wails/v3/internal/assetserver/middleware.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Middleware defines a HTTP middleware that can be applied to the AssetServer.
|
||||
// The handler passed as next is the next handler in the chain. One can decide to call the next handler
|
||||
// or implement a specialized handling.
|
||||
type Middleware func(next http.Handler) http.Handler
|
||||
|
||||
// ChainMiddleware allows chaining multiple middlewares to one middleware.
|
||||
func ChainMiddleware(middleware ...Middleware) Middleware {
|
||||
return func(h http.Handler) http.Handler {
|
||||
for i := len(middleware) - 1; i >= 0; i-- {
|
||||
h = middleware[i](h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
}
|
||||
116
vendor/github.com/wailsapp/wails/v3/internal/assetserver/mimecache.go
generated
vendored
Normal file
116
vendor/github.com/wailsapp/wails/v3/internal/assetserver/mimecache.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// mimeCache uses sync.Map for better concurrent read performance
|
||||
// since reads are far more common than writes
|
||||
mimeCache sync.Map
|
||||
|
||||
// mimeTypesByExt maps file extensions to MIME types for common web formats.
|
||||
// This approach is preferred over content-based detection because:
|
||||
// 1. Extension-based lookup is O(1) vs O(n) content scanning
|
||||
// 2. Web assets typically have correct extensions
|
||||
// 3. stdlib's http.DetectContentType handles remaining cases adequately
|
||||
// 4. Saves ~208KB binary size by not using github.com/wailsapp/mimetype
|
||||
mimeTypesByExt = map[string]string{
|
||||
// HTML
|
||||
".htm": "text/html; charset=utf-8",
|
||||
".html": "text/html; charset=utf-8",
|
||||
|
||||
// CSS/JS
|
||||
".css": "text/css; charset=utf-8",
|
||||
".js": "text/javascript; charset=utf-8",
|
||||
".mjs": "text/javascript; charset=utf-8",
|
||||
".ts": "application/x-typescript; charset=utf-8",
|
||||
".tsx": "application/x-typescript; charset=utf-8",
|
||||
".jsx": "text/javascript; charset=utf-8",
|
||||
|
||||
// Data formats
|
||||
".json": "application/json",
|
||||
".xml": "text/xml; charset=utf-8",
|
||||
".yaml": "text/yaml; charset=utf-8",
|
||||
".yml": "text/yaml; charset=utf-8",
|
||||
".toml": "text/toml; charset=utf-8",
|
||||
|
||||
// Images
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
".webp": "image/webp",
|
||||
".avif": "image/avif",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".bmp": "image/bmp",
|
||||
".tiff": "image/tiff",
|
||||
".tif": "image/tiff",
|
||||
|
||||
// Fonts
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
".ttf": "font/ttf",
|
||||
".otf": "font/otf",
|
||||
".eot": "application/vnd.ms-fontobject",
|
||||
|
||||
// Audio
|
||||
".mp3": "audio/mpeg",
|
||||
".wav": "audio/wav",
|
||||
".ogg": "audio/ogg",
|
||||
".m4a": "audio/mp4",
|
||||
".aac": "audio/aac",
|
||||
".flac": "audio/flac",
|
||||
".opus": "audio/opus",
|
||||
|
||||
// Video
|
||||
".mp4": "video/mp4",
|
||||
".webm": "video/webm",
|
||||
".ogv": "video/ogg",
|
||||
".mov": "video/quicktime",
|
||||
".avi": "video/x-msvideo",
|
||||
".mkv": "video/x-matroska",
|
||||
".m4v": "video/mp4",
|
||||
|
||||
// Documents
|
||||
".pdf": "application/pdf",
|
||||
".txt": "text/plain; charset=utf-8",
|
||||
".md": "text/markdown; charset=utf-8",
|
||||
|
||||
// Archives
|
||||
".zip": "application/zip",
|
||||
".gz": "application/gzip",
|
||||
".tar": "application/x-tar",
|
||||
|
||||
// WebAssembly
|
||||
".wasm": "application/wasm",
|
||||
|
||||
// Source maps
|
||||
".map": "application/json",
|
||||
}
|
||||
)
|
||||
|
||||
// "application/octet-stream".
|
||||
func GetMimetype(filename string, data []byte) string {
|
||||
// Fast path: check extension map first (no lock needed)
|
||||
if result := mimeTypesByExt[filepath.Ext(filename)]; result != "" {
|
||||
return result
|
||||
}
|
||||
|
||||
// Check cache (lock-free read)
|
||||
if cached, ok := mimeCache.Load(filename); ok {
|
||||
return cached.(string)
|
||||
}
|
||||
|
||||
// Slow path: use stdlib content-based detection and cache
|
||||
result := http.DetectContentType(data)
|
||||
if result == "" {
|
||||
result = "application/octet-stream"
|
||||
}
|
||||
|
||||
mimeCache.Store(filename, result)
|
||||
return result
|
||||
}
|
||||
38
vendor/github.com/wailsapp/wails/v3/internal/assetserver/options.go
generated
vendored
Normal file
38
vendor/github.com/wailsapp/wails/v3/internal/assetserver/options.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Options defines the configuration of the AssetServer.
|
||||
type Options struct {
|
||||
// Handler which serves all the content to the WebView.
|
||||
Handler http.Handler
|
||||
|
||||
// Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default
|
||||
// request handler dynamically, e.g. implement specialized Routing etc.
|
||||
// The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default
|
||||
// handler used by the AssetServer as an argument.
|
||||
//
|
||||
// This middleware injects itself before any of Wails internal middlewares.
|
||||
//
|
||||
// If not defined, the default AssetServer request chain is executed.
|
||||
//
|
||||
// Multiple Middlewares can be chained together with:
|
||||
// ChainMiddleware(middleware ...Middleware) Middleware
|
||||
Middleware Middleware
|
||||
|
||||
// Logger is the logger used by the AssetServer. If not defined, no logging will be done.
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
// Validate the options
|
||||
func (o Options) Validate() error {
|
||||
if o.Handler == nil && o.Middleware == nil {
|
||||
return errors.New("AssetServer options invalid: either Handler or Middleware must be set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
101
vendor/github.com/wailsapp/wails/v3/internal/assetserver/ringqueue.go
generated
vendored
Normal file
101
vendor/github.com/wailsapp/wails/v3/internal/assetserver/ringqueue.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// Code from https://github.com/erikdubbelboer/ringqueue
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Erik Dubbelboer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package assetserver
|
||||
|
||||
type ringqueue[T any] struct {
|
||||
nodes []T
|
||||
head int
|
||||
tail int
|
||||
cnt int
|
||||
|
||||
minSize int
|
||||
}
|
||||
|
||||
func newRingqueue[T any](minSize uint) *ringqueue[T] {
|
||||
if minSize < 2 {
|
||||
minSize = 2
|
||||
}
|
||||
return &ringqueue[T]{
|
||||
nodes: make([]T, minSize),
|
||||
minSize: int(minSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) resize(n int) {
|
||||
nodes := make([]T, n)
|
||||
if q.head < q.tail {
|
||||
copy(nodes, q.nodes[q.head:q.tail])
|
||||
} else {
|
||||
copy(nodes, q.nodes[q.head:])
|
||||
copy(nodes[len(q.nodes)-q.head:], q.nodes[:q.tail])
|
||||
}
|
||||
|
||||
q.tail = q.cnt % n
|
||||
q.head = 0
|
||||
q.nodes = nodes
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Add(i T) {
|
||||
if q.cnt == len(q.nodes) {
|
||||
// Also tested a grow rate of 1.5, see: http://stackoverflow.com/questions/2269063/buffer-growth-strategy
|
||||
// In Go this resulted in a higher memory usage.
|
||||
q.resize(q.cnt * 2)
|
||||
}
|
||||
q.nodes[q.tail] = i
|
||||
q.tail = (q.tail + 1) % len(q.nodes)
|
||||
q.cnt++
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Peek() (T, bool) {
|
||||
if q.cnt == 0 {
|
||||
var none T
|
||||
return none, false
|
||||
}
|
||||
return q.nodes[q.head], true
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Remove() (T, bool) {
|
||||
if q.cnt == 0 {
|
||||
var none T
|
||||
return none, false
|
||||
}
|
||||
i := q.nodes[q.head]
|
||||
q.head = (q.head + 1) % len(q.nodes)
|
||||
q.cnt--
|
||||
|
||||
if n := len(q.nodes) / 2; n > q.minSize && q.cnt <= n {
|
||||
q.resize(n)
|
||||
}
|
||||
|
||||
return i, true
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Cap() int {
|
||||
return cap(q.nodes)
|
||||
}
|
||||
|
||||
func (q *ringqueue[T]) Len() int {
|
||||
return q.cnt
|
||||
}
|
||||
152
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/mainthread_linux.go
generated
vendored
Normal file
152
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/mainthread_linux.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
//go:build linux && cgo && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: glib-2.0
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void webviewMainThreadCallback(uintptr_t id);
|
||||
|
||||
// webview_dispatch_mu serializes the enabled-check plus scheduling in
|
||||
// webview_invoke_on_main_sync against the flag clear in
|
||||
// webview_disable_main_dispatch. Without it a worker could read
|
||||
// enabled == TRUE, then have the flag flipped before it scheduled its source,
|
||||
// and still queue work onto the now-dead main loop — blocking forever. A
|
||||
// statically allocated GMutex needs no g_mutex_init.
|
||||
static GMutex webview_dispatch_mu;
|
||||
|
||||
// webview_main_dispatch_enabled gates whether webview_invoke_on_main_sync may
|
||||
// schedule work onto the GTK main loop. It starts enabled and is cleared once
|
||||
// the loop has stopped (see webview_disable_main_dispatch). It is only ever read
|
||||
// or written while holding webview_dispatch_mu.
|
||||
static gboolean webview_main_dispatch_enabled = TRUE;
|
||||
|
||||
static void webview_disable_main_dispatch(void) {
|
||||
g_mutex_lock(&webview_dispatch_mu);
|
||||
webview_main_dispatch_enabled = FALSE;
|
||||
g_mutex_unlock(&webview_dispatch_mu);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uintptr_t id;
|
||||
GMutex mutex;
|
||||
GCond cond;
|
||||
gboolean done;
|
||||
} webviewMainSyncCall;
|
||||
|
||||
// webview_main_sync_trampoline runs on the GTK main thread (scheduled via
|
||||
// g_main_context_invoke). It invokes the Go callback, then signals the waiting
|
||||
// worker. g_cond_signal happens while the mutex is held and before the unlock,
|
||||
// so the waiter cannot re-acquire the mutex (and destroy the primitives) until
|
||||
// the signal has completed — making the stack-allocated GMutex/GCond safe.
|
||||
static gboolean webview_main_sync_trampoline(gpointer data) {
|
||||
webviewMainSyncCall *call = (webviewMainSyncCall *)data;
|
||||
webviewMainThreadCallback(call->id);
|
||||
g_mutex_lock(&call->mutex);
|
||||
call->done = TRUE;
|
||||
g_cond_signal(&call->cond);
|
||||
g_mutex_unlock(&call->mutex);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
// webview_invoke_on_main_sync schedules the Go callback identified by id on the
|
||||
// default GTK main context and blocks the calling thread until it has finished.
|
||||
//
|
||||
// WebKit2GTK objects may only be touched on the thread running the GTK main loop
|
||||
// (g_application_run). Asset-server responses are produced on worker goroutines,
|
||||
// so the WebKit calls that complete a request must hop here first. The wait is
|
||||
// safe because webkit_uri_scheme_request_finish_with_response returns before the
|
||||
// response stream is drained (WebKit reads it asynchronously), so the main loop
|
||||
// never blocks waiting on the worker.
|
||||
//
|
||||
// If the caller is already the main thread, g_main_context_invoke runs the
|
||||
// trampoline inline, so the wait completes immediately without deadlocking.
|
||||
static void webview_invoke_on_main_sync(uintptr_t id) {
|
||||
webviewMainSyncCall call;
|
||||
call.id = id;
|
||||
call.done = FALSE;
|
||||
g_mutex_init(&call.mutex);
|
||||
g_cond_init(&call.cond);
|
||||
|
||||
// The enabled-check and the g_main_context_invoke that acts on it must be
|
||||
// atomic with respect to webview_disable_main_dispatch. Holding
|
||||
// webview_dispatch_mu across both means a worker either schedules onto a live
|
||||
// loop or sees the loop already stopped — it can never schedule onto a loop
|
||||
// that stops in between (which would block it here forever). The trampoline
|
||||
// only touches the per-call mutex/cond, never webview_dispatch_mu, so when
|
||||
// g_main_context_invoke runs it inline (main-thread caller) there is no
|
||||
// self-deadlock.
|
||||
g_mutex_lock(&webview_dispatch_mu);
|
||||
if (!webview_main_dispatch_enabled) {
|
||||
// The GTK main loop has stopped: a scheduled source would never run. The
|
||||
// loop is no longer iterating, so the cross-thread race that makes
|
||||
// main-thread confinement necessary is gone — running the callback inline
|
||||
// on the worker lets in-flight asset requests drain during shutdown
|
||||
// instead of wedging. See #5631 (review question 5).
|
||||
g_mutex_unlock(&webview_dispatch_mu);
|
||||
webviewMainThreadCallback(id);
|
||||
g_mutex_clear(&call.mutex);
|
||||
g_cond_clear(&call.cond);
|
||||
return;
|
||||
}
|
||||
g_main_context_invoke(NULL, webview_main_sync_trampoline, &call);
|
||||
g_mutex_unlock(&webview_dispatch_mu);
|
||||
|
||||
g_mutex_lock(&call.mutex);
|
||||
while (!call.done) {
|
||||
g_cond_wait(&call.cond, &call.mutex);
|
||||
}
|
||||
g_mutex_unlock(&call.mutex);
|
||||
|
||||
g_mutex_clear(&call.mutex);
|
||||
g_cond_clear(&call.cond);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
mainSyncMu sync.Mutex
|
||||
mainSyncNextID uintptr
|
||||
mainSyncCallbacks = map[uintptr]func(){}
|
||||
)
|
||||
|
||||
// invokeOnMainSync runs fn on the GTK main thread and blocks until it returns.
|
||||
// It is safe to call from any goroutine, including the main thread itself.
|
||||
func invokeOnMainSync(fn func()) {
|
||||
mainSyncMu.Lock()
|
||||
mainSyncNextID++
|
||||
id := mainSyncNextID
|
||||
mainSyncCallbacks[id] = fn
|
||||
mainSyncMu.Unlock()
|
||||
|
||||
C.webview_invoke_on_main_sync(C.uintptr_t(id))
|
||||
}
|
||||
|
||||
// DisableMainThreadDispatch marks the GTK main loop as stopped. After it is
|
||||
// called, invokeOnMainSync runs callbacks inline on the calling goroutine
|
||||
// instead of scheduling them onto the now-dead main loop, so asset-server
|
||||
// workers that complete a request during shutdown cannot block forever waiting
|
||||
// for a source that will never be serviced. The application layer calls this
|
||||
// once g_application_run has returned. See issue #5631.
|
||||
func DisableMainThreadDispatch() {
|
||||
C.webview_disable_main_dispatch()
|
||||
}
|
||||
|
||||
//export webviewMainThreadCallback
|
||||
func webviewMainThreadCallback(id C.uintptr_t) {
|
||||
mainSyncMu.Lock()
|
||||
fn := mainSyncCallbacks[uintptr(id)]
|
||||
delete(mainSyncCallbacks, uintptr(id))
|
||||
mainSyncMu.Unlock()
|
||||
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
90
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/mainthread_testsupport_linux.go
generated
vendored
Normal file
90
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/mainthread_testsupport_linux.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
//go:build linux && cgo && !android
|
||||
|
||||
package webview
|
||||
|
||||
// This file provides cgo helpers for mainthread_linux_test.go. Go forbids cgo
|
||||
// (an `import "C"` preamble) inside _test.go files, so the small GLib main-loop
|
||||
// scaffolding the test needs lives here instead. None of it is referenced by
|
||||
// production code paths.
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: glib-2.0
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
// All shared loop state is published and read under webview_test_mu so the
|
||||
// worker goroutines that call the helpers below never observe it across threads
|
||||
// without synchronization. webview_test_ready is signalled from inside the loop
|
||||
// (via an idle source) so waiters block on the cond instead of spinning on an
|
||||
// unsynchronized flag.
|
||||
static GMutex webview_test_mu;
|
||||
static GCond webview_test_cond;
|
||||
static GMainLoop *webview_test_loop; // guarded by webview_test_mu
|
||||
static GThread *webview_test_loop_thread; // guarded by webview_test_mu
|
||||
static gboolean webview_test_ready; // guarded by webview_test_mu
|
||||
|
||||
// webview_test_mark_ready runs on the loop thread once the loop starts
|
||||
// iterating, publishing that it is live.
|
||||
static gboolean webview_test_mark_ready(gpointer data) {
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
webview_test_ready = TRUE;
|
||||
g_cond_signal(&webview_test_cond);
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
// webview_test_run_loop records the running thread and drives the default GLib
|
||||
// main context, mimicking the GTK main loop that invokeOnMainSync dispatches to.
|
||||
static void webview_test_run_loop(void) {
|
||||
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
|
||||
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
webview_test_loop = loop;
|
||||
webview_test_loop_thread = g_thread_self();
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
|
||||
g_idle_add(webview_test_mark_ready, NULL);
|
||||
g_main_loop_run(loop);
|
||||
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
webview_test_loop = NULL;
|
||||
webview_test_ready = FALSE;
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
|
||||
g_main_loop_unref(loop);
|
||||
}
|
||||
|
||||
static void webview_test_wait_running(void) {
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
while (!webview_test_ready) {
|
||||
g_cond_wait(&webview_test_cond, &webview_test_mu);
|
||||
}
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
}
|
||||
|
||||
static void webview_test_quit_loop(void) {
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
GMainLoop *loop = webview_test_loop;
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
if (loop != NULL) {
|
||||
g_main_loop_quit(loop);
|
||||
}
|
||||
}
|
||||
|
||||
// webview_test_on_loop_thread reports whether the caller is the thread that runs
|
||||
// the main loop.
|
||||
static int webview_test_on_loop_thread(void) {
|
||||
g_mutex_lock(&webview_test_mu);
|
||||
GThread *loopThread = webview_test_loop_thread;
|
||||
g_mutex_unlock(&webview_test_mu);
|
||||
return g_thread_self() == loopThread ? 1 : 0;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func testRunMainLoop() { C.webview_test_run_loop() }
|
||||
func testWaitLoopRunning() { C.webview_test_wait_running() }
|
||||
func testQuitMainLoop() { C.webview_test_quit_loop() }
|
||||
func testOnLoopThread() bool {
|
||||
return C.webview_test_on_loop_thread() != 0
|
||||
}
|
||||
17
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request.go
generated
vendored
Normal file
17
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Request interface {
|
||||
URL() (string, error)
|
||||
Method() (string, error)
|
||||
Header() (http.Header, error)
|
||||
Body() (io.ReadCloser, error)
|
||||
|
||||
Response() ResponseWriter
|
||||
|
||||
Close() error
|
||||
}
|
||||
102
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_android.go
generated
vendored
Normal file
102
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_android.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//go:build android
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Request interface for Android asset requests
|
||||
// On Android, requests are handled via JNI from Java's WebViewAssetLoader
|
||||
|
||||
// androidRequest implements the Request interface for Android
|
||||
type androidRequest struct {
|
||||
url string
|
||||
method string
|
||||
headers http.Header
|
||||
body io.ReadCloser
|
||||
rw *androidResponseWriter
|
||||
}
|
||||
|
||||
// NewRequestFromJNI creates a new request from JNI parameters
|
||||
func NewRequestFromJNI(url string, method string, headersJSON string) Request {
|
||||
return &androidRequest{
|
||||
url: url,
|
||||
method: method,
|
||||
headers: http.Header{},
|
||||
body: http.NoBody,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *androidRequest) URL() (string, error) {
|
||||
return r.url, nil
|
||||
}
|
||||
|
||||
func (r *androidRequest) Method() (string, error) {
|
||||
return r.method, nil
|
||||
}
|
||||
|
||||
func (r *androidRequest) Header() (http.Header, error) {
|
||||
return r.headers, nil
|
||||
}
|
||||
|
||||
func (r *androidRequest) Body() (io.ReadCloser, error) {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *androidRequest) Response() ResponseWriter {
|
||||
if r.rw == nil {
|
||||
r.rw = &androidResponseWriter{}
|
||||
}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *androidRequest) Close() error {
|
||||
if r.body != nil {
|
||||
return r.body.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// androidResponseWriter implements ResponseWriter for Android
|
||||
type androidResponseWriter struct {
|
||||
statusCode int
|
||||
headers http.Header
|
||||
body bytes.Buffer
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (w *androidResponseWriter) Header() http.Header {
|
||||
if w.headers == nil {
|
||||
w.headers = http.Header{}
|
||||
}
|
||||
return w.headers
|
||||
}
|
||||
|
||||
func (w *androidResponseWriter) Write(data []byte) (int, error) {
|
||||
return w.body.Write(data)
|
||||
}
|
||||
|
||||
func (w *androidResponseWriter) WriteHeader(statusCode int) {
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
|
||||
func (w *androidResponseWriter) Finish() error {
|
||||
w.finished = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Code returns the HTTP status code of the response
|
||||
func (w *androidResponseWriter) Code() int {
|
||||
if w.statusCode == 0 {
|
||||
return 200
|
||||
}
|
||||
return w.statusCode
|
||||
}
|
||||
|
||||
// GetResponseData returns the response data for JNI
|
||||
func (w *androidResponseWriter) GetResponseData() []byte {
|
||||
return w.body.Bytes()
|
||||
}
|
||||
250
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_darwin.go
generated
vendored
Normal file
250
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
//go:build darwin && !ios
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#include <string.h>
|
||||
|
||||
static void URLSchemeTaskRetain(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
[urlSchemeTask retain];
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRelease(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
[urlSchemeTask release];
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestURL(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.URL.absoluteString UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestMethod(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.HTTPMethod UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestHeadersJSON(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
NSData *headerData = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil];
|
||||
if (!headerData) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString* headerString = [[[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding] autorelease];
|
||||
const char * headerJSON = [headerString UTF8String];
|
||||
|
||||
return strdup(headerJSON);
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyBytes(void *wkUrlSchemeTask, const void **body, int *bodyLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBody) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*body = urlSchemeTask.request.HTTPBody.bytes;
|
||||
*bodyLen = urlSchemeTask.request.HTTPBody.length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyStreamOpen(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream open];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRequestBodyStreamClose(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream close];
|
||||
}
|
||||
}
|
||||
|
||||
static int URLSchemeTaskRequestBodyStreamRead(void *wkUrlSchemeTask, void *buf, int bufLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
|
||||
@autoreleasepool {
|
||||
NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream;
|
||||
if (!stream) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
NSStreamStatus status = stream.streamStatus;
|
||||
if (status == NSStreamStatusAtEnd || !stream.hasBytesAvailable) {
|
||||
return 0;
|
||||
} else if (status != NSStreamStatusOpen) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return [stream read:buf maxLength:bufLen];
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest based on a pointer to an `id<WKURLSchemeTask>`
|
||||
func NewRequest(wkURLSchemeTask unsafe.Pointer) Request {
|
||||
C.URLSchemeTaskRetain(wkURLSchemeTask)
|
||||
return newRequestFinalizer(&request{task: wkURLSchemeTask})
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
task unsafe.Pointer
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestMethod(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
if cHeaders := C.URLSchemeTaskRequestHeadersJSON(r.task); cHeaders != nil {
|
||||
if headers := C.GoString(cHeaders); headers != "" {
|
||||
var h map[string]string
|
||||
if err := json.Unmarshal([]byte(headers), &h); err != nil {
|
||||
return nil, fmt.Errorf("unable to unmarshal request headers: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range h {
|
||||
header.Add(k, v)
|
||||
}
|
||||
}
|
||||
C.free(unsafe.Pointer(cHeaders))
|
||||
}
|
||||
r.header = header
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
var body unsafe.Pointer
|
||||
var bodyLen C.int
|
||||
if C.URLSchemeTaskRequestBodyBytes(r.task, &body, &bodyLen) {
|
||||
if body != nil && bodyLen > 0 {
|
||||
r.body = io.NopCloser(bytes.NewReader(C.GoBytes(body, bodyLen)))
|
||||
} else {
|
||||
r.body = http.NoBody
|
||||
}
|
||||
} else if C.URLSchemeTaskRequestBodyStreamOpen(r.task) {
|
||||
r.body = &requestBodyStreamReader{task: r.task}
|
||||
}
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{r: r}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
r.Response().Finish()
|
||||
C.URLSchemeTaskRelease(r.task)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = &requestBodyStreamReader{}
|
||||
|
||||
type requestBodyStreamReader struct {
|
||||
task unsafe.Pointer
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *requestBodyStreamReader) Read(p []byte) (n int, err error) {
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if p != nil {
|
||||
content = unsafe.Pointer(&p[0])
|
||||
contentLen = len(p)
|
||||
}
|
||||
|
||||
res := C.URLSchemeTaskRequestBodyStreamRead(r.task, content, C.int(contentLen))
|
||||
if res > 0 {
|
||||
return int(res), nil
|
||||
}
|
||||
|
||||
switch res {
|
||||
case 0:
|
||||
return 0, io.EOF
|
||||
case -1:
|
||||
return 0, errors.New("body: stream error")
|
||||
case -2:
|
||||
return 0, errors.New("body: no stream defined")
|
||||
case -3:
|
||||
return 0, io.ErrClosedPipe
|
||||
default:
|
||||
return 0, fmt.Errorf("body: unknown error %d", res)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *requestBodyStreamReader) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
C.URLSchemeTaskRequestBodyStreamClose(r.task)
|
||||
return nil
|
||||
}
|
||||
40
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_finalizer.go
generated
vendored
Normal file
40
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_finalizer.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var _ Request = &requestFinalizer{}
|
||||
|
||||
type requestFinalizer struct {
|
||||
Request
|
||||
closed int32
|
||||
}
|
||||
|
||||
// newRequestFinalizer returns a request with a runtime finalizer to make sure it will be closed from the finalizer
|
||||
// if it has not been already closed.
|
||||
// It also makes sure Close() of the wrapping request is only called once.
|
||||
func newRequestFinalizer(r Request) Request {
|
||||
rf := &requestFinalizer{Request: r}
|
||||
// Make sure to async release since it might block the finalizer goroutine for a longer period
|
||||
runtime.SetFinalizer(rf, func(obj *requestFinalizer) { rf.close(true) })
|
||||
return rf
|
||||
}
|
||||
|
||||
func (r *requestFinalizer) Close() error {
|
||||
return r.close(false)
|
||||
}
|
||||
|
||||
func (r *requestFinalizer) close(asyncRelease bool) error {
|
||||
if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
|
||||
runtime.SetFinalizer(r, nil)
|
||||
if asyncRelease {
|
||||
go r.Request.Close()
|
||||
return nil
|
||||
} else {
|
||||
return r.Request.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
248
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_ios.go
generated
vendored
Normal file
248
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_ios.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
//go:build ios
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c -fobjc-arc
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit -framework CoreFoundation
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#include <string.h>
|
||||
|
||||
static void URLSchemeTaskRetain(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
CFRetain((CFTypeRef)urlSchemeTask);
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRelease(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
CFRelease((CFTypeRef)urlSchemeTask);
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestURL(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.URL.absoluteString UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestMethod(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
return [urlSchemeTask.request.HTTPMethod UTF8String];
|
||||
}
|
||||
}
|
||||
|
||||
static const char * URLSchemeTaskRequestHeadersJSON(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
NSData *headerData = [NSJSONSerialization dataWithJSONObject:urlSchemeTask.request.allHTTPHeaderFields options:0 error:nil];
|
||||
if (!headerData) {
|
||||
return nil;
|
||||
}
|
||||
NSString *headerString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
|
||||
const char *headerJSON = [headerString UTF8String];
|
||||
return strdup(headerJSON);
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyBytes(void *wkUrlSchemeTask, const void **body, int *bodyLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBody) {
|
||||
return false;
|
||||
}
|
||||
*body = urlSchemeTask.request.HTTPBody.bytes;
|
||||
*bodyLen = urlSchemeTask.request.HTTPBody.length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskRequestBodyStreamOpen(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream open];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void URLSchemeTaskRequestBodyStreamClose(void *wkUrlSchemeTask) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
@autoreleasepool {
|
||||
if (!urlSchemeTask.request.HTTPBodyStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
[urlSchemeTask.request.HTTPBodyStream close];
|
||||
}
|
||||
}
|
||||
|
||||
static int URLSchemeTaskRequestBodyStreamRead(void *wkUrlSchemeTask, void *buf, int bufLen) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
|
||||
@autoreleasepool {
|
||||
NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream;
|
||||
if (!stream) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
NSStreamStatus status = stream.streamStatus;
|
||||
if (status == NSStreamStatusAtEnd || !stream.hasBytesAvailable) {
|
||||
return 0;
|
||||
} else if (status != NSStreamStatusOpen) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return [stream read:buf maxLength:bufLen];
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest based on a pointer to an `id<WKURLSchemeTask>`
|
||||
func NewRequest(wkURLSchemeTask unsafe.Pointer) Request {
|
||||
C.URLSchemeTaskRetain(wkURLSchemeTask)
|
||||
return newRequestFinalizer(&request{task: wkURLSchemeTask})
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
task unsafe.Pointer
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return C.GoString(C.URLSchemeTaskRequestMethod(r.task)), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
if cHeaders := C.URLSchemeTaskRequestHeadersJSON(r.task); cHeaders != nil {
|
||||
if headers := C.GoString(cHeaders); headers != "" {
|
||||
var h map[string]string
|
||||
if err := json.Unmarshal([]byte(headers), &h); err != nil {
|
||||
return nil, fmt.Errorf("unable to unmarshal request headers: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range h {
|
||||
header.Add(k, v)
|
||||
}
|
||||
}
|
||||
C.free(unsafe.Pointer(cHeaders))
|
||||
}
|
||||
r.header = header
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
var body unsafe.Pointer
|
||||
var bodyLen C.int
|
||||
if C.URLSchemeTaskRequestBodyBytes(r.task, &body, &bodyLen) {
|
||||
if body != nil && bodyLen > 0 {
|
||||
r.body = io.NopCloser(bytes.NewReader(C.GoBytes(body, bodyLen)))
|
||||
} else {
|
||||
r.body = http.NoBody
|
||||
}
|
||||
} else if C.URLSchemeTaskRequestBodyStreamOpen(r.task) {
|
||||
r.body = &requestBodyStreamReader{task: r.task}
|
||||
}
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{r: r}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
r.Response().Finish()
|
||||
C.URLSchemeTaskRelease(r.task)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = &requestBodyStreamReader{}
|
||||
|
||||
type requestBodyStreamReader struct {
|
||||
task unsafe.Pointer
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *requestBodyStreamReader) Read(p []byte) (n int, err error) {
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if p != nil {
|
||||
content = unsafe.Pointer(&p[0])
|
||||
contentLen = len(p)
|
||||
}
|
||||
|
||||
res := C.URLSchemeTaskRequestBodyStreamRead(r.task, content, C.int(contentLen))
|
||||
if res > 0 {
|
||||
return int(res), nil
|
||||
}
|
||||
|
||||
switch res {
|
||||
case 0:
|
||||
return 0, io.EOF
|
||||
case -1:
|
||||
return 0, errors.New("body: stream error")
|
||||
case -2:
|
||||
return 0, errors.New("body: no stream defined")
|
||||
case -3:
|
||||
return 0, io.ErrClosedPipe
|
||||
default:
|
||||
return 0, fmt.Errorf("body: unknown error %d", res)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *requestBodyStreamReader) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
C.URLSchemeTaskRequestBodyStreamClose(r.task)
|
||||
return nil
|
||||
}
|
||||
106
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_linux.go
generated
vendored
Normal file
106
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_linux.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
//go:build linux && cgo && !gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk4 webkitgtk-6.0 gio-unix-2.0
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit/webkit.h>
|
||||
|
||||
static gboolean unref_request_on_main(gpointer data) {
|
||||
if (data != NULL) {
|
||||
g_object_unref(data);
|
||||
}
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
// releaseRequestOnMainThread schedules the WebKitURISchemeRequest unref on the
|
||||
// GTK main context. Close() runs on the assetserver goroutine, and dropping
|
||||
// what may be the last reference finalizes a WebKit GObject — only safe on the
|
||||
// UI thread (see #5557).
|
||||
static void releaseRequestOnMainThread(WebKitURISchemeRequest *request) {
|
||||
if (request == NULL) {
|
||||
return;
|
||||
}
|
||||
g_main_context_invoke(NULL, unref_request_on_main, request);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request {
|
||||
webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest)
|
||||
C.g_object_ref(C.gpointer(webkitReq))
|
||||
|
||||
req := &request{req: webkitReq}
|
||||
return newRequestFinalizer(req)
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
// Reading the URI touches the WebKit-owned request on the GTK main loop;
|
||||
// this runs on a worker goroutine, so it must hop to the main thread.
|
||||
// See mainthread_linux.go and issue #5631.
|
||||
var uri string
|
||||
invokeOnMainSync(func() {
|
||||
uri = C.GoString(C.webkit_uri_scheme_request_get_uri(r.req))
|
||||
})
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return webkit_uri_scheme_request_get_http_method(r.req), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
r.header = webkit_uri_scheme_request_get_http_headers(r.req)
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
r.body = webkit_uri_scheme_request_get_http_body(r.req)
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r.req}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
r.Response().Finish()
|
||||
C.releaseRequestOnMainThread(r.req)
|
||||
return err
|
||||
}
|
||||
107
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_linux_gtk3.go
generated
vendored
Normal file
107
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_linux_gtk3.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
//go:build linux && cgo && gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 gio-unix-2.0
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
|
||||
static gboolean unref_request_on_main(gpointer data) {
|
||||
if (data != NULL) {
|
||||
g_object_unref(data);
|
||||
}
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
// releaseRequestOnMainThread schedules the WebKitURISchemeRequest unref on the
|
||||
// GTK main context. Close() runs on the assetserver goroutine, and dropping
|
||||
// what may be the last reference finalizes a WebKit GObject — only safe on the
|
||||
// UI thread (see #5557).
|
||||
static void releaseRequestOnMainThread(WebKitURISchemeRequest *request) {
|
||||
if (request == NULL) {
|
||||
return;
|
||||
}
|
||||
g_main_context_invoke(NULL, unref_request_on_main, request);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest`
|
||||
func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request {
|
||||
webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest)
|
||||
C.g_object_ref(C.gpointer(webkitReq))
|
||||
|
||||
req := &request{req: webkitReq}
|
||||
return newRequestFinalizer(req)
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
body io.ReadCloser
|
||||
rw *responseWriter
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
// Reading the URI touches the WebKit-owned request on the GTK main loop;
|
||||
// this runs on a worker goroutine, so it must hop to the main thread.
|
||||
// See mainthread_linux.go and issue #5631.
|
||||
var uri string
|
||||
invokeOnMainSync(func() {
|
||||
uri = C.GoString(C.webkit_uri_scheme_request_get_uri(r.req))
|
||||
})
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return webkit_uri_scheme_request_get_http_method(r.req), nil
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
if r.header != nil {
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
r.header = webkit_uri_scheme_request_get_http_headers(r.req)
|
||||
return r.header, nil
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
if r.body != nil {
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
r.body = webkit_uri_scheme_request_get_http_body(r.req)
|
||||
|
||||
return r.body, nil
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r.req}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var err error
|
||||
if r.body != nil {
|
||||
err = r.body.Close()
|
||||
}
|
||||
r.Response().Finish()
|
||||
C.releaseRequestOnMainThread(r.req)
|
||||
return err
|
||||
}
|
||||
218
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_windows.go
generated
vendored
Normal file
218
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/request_windows.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
//go:build windows
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/wailsapp/wails/v3/internal/webview2/pkg/edge"
|
||||
)
|
||||
|
||||
// NewRequest creates as new WebViewRequest for chromium. This Method must be called from the Main-Thread!
|
||||
func NewRequest(env *edge.ICoreWebView2Environment, args *edge.ICoreWebView2WebResourceRequestedEventArgs, invokeSync func(fn func())) (Request, error) {
|
||||
req, err := args.GetRequest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRequest failed: %s", err)
|
||||
}
|
||||
defer req.Release()
|
||||
|
||||
r := &request{
|
||||
invokeSync: invokeSync,
|
||||
}
|
||||
|
||||
code := http.StatusInternalServerError
|
||||
r.response, err = env.CreateWebResourceResponse(nil, code, http.StatusText(code), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CreateWebResourceResponse failed: %s", err)
|
||||
}
|
||||
|
||||
if err := args.PutResponse(r.response); err != nil {
|
||||
r.finishResponse()
|
||||
return nil, fmt.Errorf("PutResponse failed: %s", err)
|
||||
}
|
||||
|
||||
r.deferral, err = args.GetDeferral()
|
||||
if err != nil {
|
||||
r.finishResponse()
|
||||
return nil, fmt.Errorf("GetDeferral failed: %s", err)
|
||||
}
|
||||
|
||||
r.url, r.urlErr = req.GetUri()
|
||||
r.method, r.methodErr = req.GetMethod()
|
||||
r.header, r.headerErr = getHeaders(req)
|
||||
|
||||
if content, err := req.GetContent(); err != nil {
|
||||
r.bodyErr = err
|
||||
} else if content != nil {
|
||||
// It is safe to access Content from another Thread: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#thread-safety
|
||||
r.body = &iStreamReleaseCloser{stream: content}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
var _ Request = &request{}
|
||||
|
||||
type request struct {
|
||||
response *edge.ICoreWebView2WebResourceResponse
|
||||
deferral *edge.ICoreWebView2Deferral
|
||||
|
||||
url string
|
||||
urlErr error
|
||||
|
||||
method string
|
||||
methodErr error
|
||||
|
||||
header http.Header
|
||||
headerErr error
|
||||
|
||||
body io.ReadCloser
|
||||
bodyErr error
|
||||
rw *responseWriter
|
||||
|
||||
invokeSync func(fn func())
|
||||
}
|
||||
|
||||
func (r *request) URL() (string, error) {
|
||||
return r.url, r.urlErr
|
||||
}
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
return r.method, r.methodErr
|
||||
}
|
||||
|
||||
func (r *request) Header() (http.Header, error) {
|
||||
return r.header, r.headerErr
|
||||
}
|
||||
|
||||
func (r *request) Body() (io.ReadCloser, error) {
|
||||
return r.body, r.bodyErr
|
||||
}
|
||||
|
||||
func (r *request) Response() ResponseWriter {
|
||||
if r.rw != nil {
|
||||
return r.rw
|
||||
}
|
||||
|
||||
r.rw = &responseWriter{req: r}
|
||||
return r.rw
|
||||
}
|
||||
|
||||
func (r *request) Close() error {
|
||||
var errs []error
|
||||
if r.body != nil {
|
||||
if err := r.body.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.body = nil
|
||||
}
|
||||
|
||||
if err := r.Response().Finish(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return combineErrs(errs)
|
||||
}
|
||||
|
||||
// finishResponse must be called on the main-thread
|
||||
func (r *request) finishResponse() error {
|
||||
var errs []error
|
||||
if r.response != nil {
|
||||
if err := r.response.Release(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.response = nil
|
||||
}
|
||||
if r.deferral != nil {
|
||||
if err := r.deferral.Complete(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := r.deferral.Release(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
r.deferral = nil
|
||||
}
|
||||
return combineErrs(errs)
|
||||
}
|
||||
|
||||
type iStreamReleaseCloser struct {
|
||||
stream *edge.IStream
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (i *iStreamReleaseCloser) Read(p []byte) (int, error) {
|
||||
if i.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
return i.stream.Read(p)
|
||||
}
|
||||
|
||||
func (i *iStreamReleaseCloser) Close() error {
|
||||
if i.closed {
|
||||
return nil
|
||||
}
|
||||
i.closed = true
|
||||
return i.stream.Release()
|
||||
}
|
||||
|
||||
func getHeaders(req *edge.ICoreWebView2WebResourceRequest) (http.Header, error) {
|
||||
header := http.Header{}
|
||||
headers, err := req.GetHeaders()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetHeaders Error: %s", err)
|
||||
}
|
||||
defer headers.Release()
|
||||
|
||||
headersIt, err := headers.GetIterator()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetIterator Error: %s", err)
|
||||
}
|
||||
defer headersIt.Release()
|
||||
|
||||
for {
|
||||
has, err := headersIt.HasCurrentHeader()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("HasCurrentHeader Error: %s", err)
|
||||
}
|
||||
if !has {
|
||||
break
|
||||
}
|
||||
|
||||
name, value, err := headersIt.GetCurrentHeader()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetCurrentHeader Error: %s", err)
|
||||
}
|
||||
|
||||
header.Set(name, value)
|
||||
if _, err := headersIt.MoveNext(); err != nil {
|
||||
return nil, fmt.Errorf("MoveNext Error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other
|
||||
// requests including IPC calls.
|
||||
// So prevent 304 status codes by removing the headers that are used in combinationwith caching.
|
||||
header.Del("If-Modified-Since")
|
||||
header.Del("If-None-Match")
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func combineErrs(errs []error) error {
|
||||
err := errors.Join(errs...)
|
||||
|
||||
if err != nil {
|
||||
// errors.Join wraps even a single error.
|
||||
// Check the filtered error list,
|
||||
// and if it has just one element return it directly.
|
||||
errs = err.(interface{ Unwrap() []error }).Unwrap()
|
||||
if len(errs) == 1 {
|
||||
return errs[0]
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
28
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter.go
generated
vendored
Normal file
28
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package webview
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentType = "Content-Type"
|
||||
)
|
||||
|
||||
var (
|
||||
errRequestStopped = errors.New("request has been stopped")
|
||||
errResponseFinished = errors.New("response has been finished")
|
||||
)
|
||||
|
||||
// A ResponseWriter interface is used by an HTTP handler to
|
||||
// construct an HTTP response for the WebView.
|
||||
type ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
|
||||
// Finish the response and flush all data. A Finish after the request has already been finished has no effect.
|
||||
Finish() error
|
||||
|
||||
// Code returns the HTTP status code of the response
|
||||
Code() int
|
||||
}
|
||||
156
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_darwin.go
generated
vendored
Normal file
156
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
//go:build darwin && !ios
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>);
|
||||
|
||||
static bool urlSchemeTaskCall(void *wkUrlSchemeTask, schemeTaskCaller fn) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
if (urlSchemeTask == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
@try {
|
||||
fn(urlSchemeTask);
|
||||
} @catch (NSException *exception) {
|
||||
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
||||
// But it seems to be very tricky to not deadlock when keeping a lock curing executing fn()
|
||||
// It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want
|
||||
// to get the lock again to start another request or stop it.
|
||||
if ([exception.reason isEqualToString: @"This task has already been stopped"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveData(void *wkUrlSchemeTask, void* data, int datalength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
[urlSchemeTask didReceiveData:nsdata];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidFinish(void *wkUrlSchemeTask) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
[urlSchemeTask didFinish];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveResponse(void *wkUrlSchemeTask, int statusCode, void *headersString, int headersStringLength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength];
|
||||
NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData:nsHeadersJSON options: NSJSONReadingMutableContainers error: nil];
|
||||
NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease];
|
||||
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
});
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var _ ResponseWriter = &responseWriter{}
|
||||
|
||||
type responseWriter struct {
|
||||
r *request
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
code int
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if buf != nil && len(buf) > 0 {
|
||||
content = unsafe.Pointer(&buf[0])
|
||||
contentLen = len(buf)
|
||||
}
|
||||
|
||||
if !C.URLSchemeTaskDidReceiveData(rw.r.task, content, C.int(contentLen)) {
|
||||
return 0, errRequestStopped
|
||||
}
|
||||
return contentLen, nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.code = code
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
header := map[string]string{}
|
||||
for k := range rw.Header() {
|
||||
header[k] = rw.Header().Get(k)
|
||||
}
|
||||
headerData, _ := json.Marshal(header)
|
||||
|
||||
var headers unsafe.Pointer
|
||||
var headersLen int
|
||||
if len(headerData) != 0 {
|
||||
headers = unsafe.Pointer(&headerData[0])
|
||||
headersLen = len(headerData)
|
||||
}
|
||||
|
||||
C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen))
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
|
||||
C.URLSchemeTaskDidFinish(rw.r.task)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Code() int {
|
||||
return rw.code
|
||||
}
|
||||
161
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_ios.go
generated
vendored
Normal file
161
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_ios.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
//go:build ios
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c -fobjc-arc
|
||||
#cgo LDFLAGS: -framework Foundation -framework WebKit
|
||||
|
||||
#include <stdlib.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>);
|
||||
|
||||
static bool urlSchemeTaskCall(void *wkUrlSchemeTask, schemeTaskCaller fn) {
|
||||
id<WKURLSchemeTask> urlSchemeTask = (__bridge id<WKURLSchemeTask>) wkUrlSchemeTask;
|
||||
if (urlSchemeTask == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
@try {
|
||||
fn(urlSchemeTask);
|
||||
} @catch (NSException *exception) {
|
||||
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
||||
// But it seems to be very tricky to not deadlock when keeping a lock curing executing fn()
|
||||
// It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want
|
||||
// to get the lock again to start another request or stop it.
|
||||
if ([exception.reason isEqualToString: @"This task has already been stopped"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveData(void *wkUrlSchemeTask, void* data, int datalength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
[urlSchemeTask didReceiveData:nsdata];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidFinish(void *wkUrlSchemeTask) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
[urlSchemeTask didFinish];
|
||||
});
|
||||
}
|
||||
|
||||
static bool URLSchemeTaskDidReceiveResponse(void *wkUrlSchemeTask, int statusCode, void *headersString, int headersStringLength) {
|
||||
return urlSchemeTaskCall(
|
||||
wkUrlSchemeTask,
|
||||
^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength];
|
||||
NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData:nsHeadersJSON options:NSJSONReadingMutableContainers error:nil];
|
||||
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:nil headerFields:headerFields];
|
||||
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
});
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var _ ResponseWriter = &responseWriter{}
|
||||
|
||||
type responseWriter struct {
|
||||
r *request
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
code int
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
// Guard on length, not just nil: a non-nil but empty slice (e.g. the body of
|
||||
// a bound method that returned "") would make &buf[0] panic with an
|
||||
// index-out-of-range, aborting the Go runtime. An empty body is valid — pass
|
||||
// a nil pointer with length 0, which yields an empty NSData on the C side.
|
||||
if len(buf) != 0 {
|
||||
content = unsafe.Pointer(&buf[0])
|
||||
contentLen = len(buf)
|
||||
}
|
||||
|
||||
if !C.URLSchemeTaskDidReceiveData(rw.r.task, content, C.int(contentLen)) {
|
||||
return 0, errRequestStopped
|
||||
}
|
||||
return contentLen, nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.code = code
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
header := map[string]string{}
|
||||
for k := range rw.Header() {
|
||||
header[k] = rw.Header().Get(k)
|
||||
}
|
||||
headerData, _ := json.Marshal(header)
|
||||
|
||||
var headers unsafe.Pointer
|
||||
var headersLen int
|
||||
if len(headerData) != 0 {
|
||||
headers = unsafe.Pointer(&headerData[0])
|
||||
headersLen = len(headerData)
|
||||
}
|
||||
|
||||
C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen))
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
|
||||
C.URLSchemeTaskDidFinish(rw.r.task)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Code() int {
|
||||
return rw.code
|
||||
}
|
||||
146
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_linux.go
generated
vendored
Normal file
146
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_linux.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
//go:build linux && cgo && !gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk4 webkitgtk-6.0
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit/webkit.h>
|
||||
|
||||
// webview_asset_error_quark returns a stable GError domain for asset-server
|
||||
// failures. The string literal is static storage, so g_quark_from_static_string
|
||||
// interns it once and never leaks. Interning the per-request error message
|
||||
// instead (the previous behaviour) grew the global quark table unboundedly on
|
||||
// long-running apps, since GQuarks are never freed.
|
||||
static GQuark webview_asset_error_quark(void) {
|
||||
return g_quark_from_static_string("wails-webview-assetserver");
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type responseWriter struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
finished bool
|
||||
code int
|
||||
|
||||
w io.WriteCloser
|
||||
wErr error
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Code() int {
|
||||
return rw.code
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
if rw.wErr != nil {
|
||||
return 0, rw.wErr
|
||||
}
|
||||
return rw.w.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.code = code
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
contentLength := int64(-1)
|
||||
if sLen := rw.Header().Get(HeaderContentLength); sLen != "" {
|
||||
if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 {
|
||||
contentLength = pLen
|
||||
}
|
||||
}
|
||||
|
||||
rFD, w, err := pipe()
|
||||
if err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to open pipe: %s", err))
|
||||
return
|
||||
}
|
||||
rw.w = w
|
||||
|
||||
// webkit_uri_scheme_request_finish wraps the read end of the pipe in a
|
||||
// GUnixInputStream and completes the request on the GTK main thread; the
|
||||
// stream is created and released there too. See webkit_linux.go and #5631.
|
||||
if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), rFD, contentLength); err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) finishWithError(code int, err error) {
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
rw.w = &nopCloser{io.Discard}
|
||||
}
|
||||
rw.wErr = err
|
||||
|
||||
msg := C.CString(err.Error())
|
||||
defer C.free(unsafe.Pointer(msg))
|
||||
|
||||
// webkit_uri_scheme_request_finish_error touches the WebKit-owned request
|
||||
// on the GTK main loop; this runs on an asset-server worker goroutine, so
|
||||
// it must hop to the main thread. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
gerr := C.g_error_new_literal(C.webview_asset_error_quark(), C.int(code), msg)
|
||||
C.webkit_uri_scheme_request_finish_error(rw.req, gerr)
|
||||
C.g_error_free(gerr)
|
||||
})
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func pipe() (r int, w *os.File, err error) {
|
||||
var p [2]int
|
||||
e := syscall.Pipe2(p[0:], 0)
|
||||
if e != nil {
|
||||
return 0, nil, fmt.Errorf("pipe2: %s", e)
|
||||
}
|
||||
|
||||
return p[0], os.NewFile(uintptr(p[1]), "|1"), nil
|
||||
}
|
||||
149
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_linux_gtk3.go
generated
vendored
Normal file
149
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_linux_gtk3.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
//go:build linux && cgo && gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
|
||||
// webview_asset_error_quark returns a stable GError domain for asset-server
|
||||
// failures. The string literal is static storage, so g_quark_from_static_string
|
||||
// interns it once and never leaks. Interning the per-request error message
|
||||
// instead (the previous behaviour) grew the global quark table unboundedly on
|
||||
// long-running apps, since GQuarks are never freed.
|
||||
static GQuark webview_asset_error_quark(void) {
|
||||
return g_quark_from_static_string("wails-webview-assetserver");
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type responseWriter struct {
|
||||
req *C.WebKitURISchemeRequest
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
finished bool
|
||||
code int
|
||||
|
||||
w io.WriteCloser
|
||||
wErr error
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Code() int {
|
||||
return rw.code
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
if rw.wErr != nil {
|
||||
return 0, rw.wErr
|
||||
}
|
||||
return rw.w.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.code = code
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
contentLength := int64(-1)
|
||||
if sLen := rw.Header().Get(HeaderContentLength); sLen != "" {
|
||||
if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 {
|
||||
contentLength = pLen
|
||||
}
|
||||
}
|
||||
|
||||
// We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the
|
||||
// read FD is given to the InputStream and will be closed there.
|
||||
// Furthermore we especially don't want to have the FD_CLOEXEC
|
||||
rFD, w, err := pipe()
|
||||
if err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to open pipe: %s", err))
|
||||
return
|
||||
}
|
||||
rw.w = w
|
||||
|
||||
// webkit_uri_scheme_request_finish wraps the read end of the pipe in a
|
||||
// GUnixInputStream and completes the request on the GTK main thread; the
|
||||
// stream is created and released there too. See webkit_linux_gtk3.go and #5631.
|
||||
if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), rFD, contentLength); err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *responseWriter) finishWithError(code int, err error) {
|
||||
if rw.w != nil {
|
||||
rw.w.Close()
|
||||
rw.w = &nopCloser{io.Discard}
|
||||
}
|
||||
rw.wErr = err
|
||||
|
||||
msg := C.CString(err.Error())
|
||||
defer C.free(unsafe.Pointer(msg))
|
||||
|
||||
// webkit_uri_scheme_request_finish_error touches the WebKit-owned request
|
||||
// on the GTK main loop; this runs on an asset-server worker goroutine, so
|
||||
// it must hop to the main thread. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
gerr := C.g_error_new_literal(C.webview_asset_error_quark(), C.int(code), msg)
|
||||
C.webkit_uri_scheme_request_finish_error(rw.req, gerr)
|
||||
C.g_error_free(gerr)
|
||||
})
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func pipe() (r int, w *os.File, err error) {
|
||||
var p [2]int
|
||||
e := syscall.Pipe2(p[0:], 0)
|
||||
if e != nil {
|
||||
return 0, nil, fmt.Errorf("pipe2: %s", e)
|
||||
}
|
||||
|
||||
return p[0], os.NewFile(uintptr(p[1]), "|1"), nil
|
||||
}
|
||||
109
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_windows.go
generated
vendored
Normal file
109
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/responsewriter_windows.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
|
||||
package webview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ http.ResponseWriter = &responseWriter{}
|
||||
|
||||
type responseWriter struct {
|
||||
req *request
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
code int
|
||||
body *bytes.Buffer
|
||||
|
||||
finished bool
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(buf []byte) (int, error) {
|
||||
if rw.finished {
|
||||
return 0, errResponseFinished
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
return rw.body.Write(buf)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(code int) {
|
||||
if rw.wroteHeader || rw.finished {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
if rw.body == nil {
|
||||
rw.body = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
rw.code = code
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Finish() error {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if rw.finished {
|
||||
return nil
|
||||
}
|
||||
rw.finished = true
|
||||
|
||||
var errs []error
|
||||
|
||||
code := rw.code
|
||||
if code == http.StatusNotModified {
|
||||
// WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other
|
||||
// requests including IPC calls.
|
||||
errs = append(errs, errors.New("AssetServer returned 304 - StatusNotModified which are going to hang WebView2, changed code to 505 - StatusInternalServerError"))
|
||||
code = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
rw.req.invokeSync(func() {
|
||||
resp := rw.req.response
|
||||
|
||||
hdrs, err := resp.GetHeaders()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.GetHeaders failed: %s", err))
|
||||
} else {
|
||||
for k, v := range rw.header {
|
||||
if err := hdrs.AppendHeader(k, strings.Join(v, ",")); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.AppendHeader failed: %s", err))
|
||||
}
|
||||
}
|
||||
hdrs.Release()
|
||||
}
|
||||
|
||||
if err := resp.PutStatusCode(code); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.PutStatusCode failed: %s", err))
|
||||
}
|
||||
|
||||
if err := resp.PutByteContent(rw.body.Bytes()); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.PutByteContent failed: %s", err))
|
||||
}
|
||||
|
||||
if err := rw.req.finishResponse(); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Resp.finishResponse failed: %s", err))
|
||||
}
|
||||
})
|
||||
|
||||
return combineErrs(errs)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Code() int {
|
||||
return rw.code
|
||||
}
|
||||
181
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/webkit_linux.go
generated
vendored
Normal file
181
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/webkit_linux.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
//go:build linux && cgo && !gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk4 webkitgtk-6.0 libsoup-3.0 gio-unix-2.0
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit/webkit.h>
|
||||
#include <libsoup/soup.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const Webkit2MinMinorVersion = 0
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string {
|
||||
// Reading request metadata touches the WebKit-owned request object, which
|
||||
// belongs to the GTK main loop; this runs on a worker goroutine, so it must
|
||||
// hop to the main thread. See mainthread_linux.go and issue #5631.
|
||||
var method string
|
||||
invokeOnMainSync(func() {
|
||||
method = C.GoString(C.webkit_uri_scheme_request_get_http_method(req))
|
||||
})
|
||||
return strings.ToUpper(method)
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(req *C.WebKitURISchemeRequest) http.Header {
|
||||
h := http.Header{}
|
||||
// Reading and iterating the request's libsoup headers touches WebKit-owned
|
||||
// state on the GTK main loop; this runs on a worker goroutine, so it must hop
|
||||
// to the main thread. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
hdrs := C.webkit_uri_scheme_request_get_http_headers(req)
|
||||
|
||||
var iter C.SoupMessageHeadersIter
|
||||
C.soup_message_headers_iter_init(&iter, hdrs)
|
||||
|
||||
var name *C.char
|
||||
var value *C.char
|
||||
|
||||
for C.soup_message_headers_iter_next(&iter, &name, &value) != 0 {
|
||||
h.Add(C.GoString(name), C.GoString(value))
|
||||
}
|
||||
})
|
||||
return h
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, rFD int, streamLength int64) error {
|
||||
// Completing the request touches WebKit/libsoup objects owned by the GTK
|
||||
// main loop, but this runs on an asset-server worker goroutine. WebKit2GTK
|
||||
// is not thread-safe, so the whole sequence must hop to the main thread.
|
||||
//
|
||||
// The response input stream is created and unref'd inside the same hop: it is
|
||||
// ref-taken by webkit_uri_scheme_response_new on the main thread, so creating
|
||||
// and releasing our reference here too keeps every refcount operation on a
|
||||
// single thread. Previously the stream was built and unref'd on the worker
|
||||
// while WebKit took its ref on the main thread, splitting the stream's
|
||||
// refcount across threads. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
stream := C.g_unix_input_stream_new(C.int(rFD), C.gboolean(1))
|
||||
defer C.g_object_unref(C.gpointer(stream))
|
||||
|
||||
resp := C.webkit_uri_scheme_response_new(stream, C.gint64(streamLength))
|
||||
defer C.g_object_unref(C.gpointer(resp))
|
||||
|
||||
cReason := C.CString(http.StatusText(code))
|
||||
C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason)
|
||||
C.free(unsafe.Pointer(cReason))
|
||||
|
||||
cMimeType := C.CString(header.Get(HeaderContentType))
|
||||
C.webkit_uri_scheme_response_set_content_type(resp, cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
|
||||
// Ownership of hdrs is transferred to the response by
|
||||
// webkit_uri_scheme_response_set_http_headers (transfer full), so we must
|
||||
// not unref it here — doing so frees the headers while WebKit/libsoup still
|
||||
// reference them, crashing in soup_message_headers_iter_next on render.
|
||||
hdrs := C.soup_message_headers_new(C.SOUP_MESSAGE_HEADERS_RESPONSE)
|
||||
for name, values := range header {
|
||||
cName := C.CString(name)
|
||||
for _, value := range values {
|
||||
cValue := C.CString(value)
|
||||
C.soup_message_headers_append(hdrs, cName, cValue)
|
||||
C.free(unsafe.Pointer(cValue))
|
||||
}
|
||||
C.free(unsafe.Pointer(cName))
|
||||
}
|
||||
|
||||
C.webkit_uri_scheme_response_set_http_headers(resp, hdrs)
|
||||
|
||||
C.webkit_uri_scheme_request_finish_with_response(req, resp)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(req *C.WebKitURISchemeRequest) io.ReadCloser {
|
||||
// Fetching the request body stream touches the WebKit-owned request on the
|
||||
// GTK main loop; this runs on a worker goroutine, so it must hop to the main
|
||||
// thread. See mainthread_linux.go and issue #5631.
|
||||
var stream *C.GInputStream
|
||||
invokeOnMainSync(func() {
|
||||
stream = C.webkit_uri_scheme_request_get_http_body(req)
|
||||
})
|
||||
if stream == nil {
|
||||
return http.NoBody
|
||||
}
|
||||
return &webkitRequestBody{stream: stream}
|
||||
}
|
||||
|
||||
type webkitRequestBody struct {
|
||||
stream *C.GInputStream
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (r *webkitRequestBody) Read(p []byte) (int, error) {
|
||||
if r.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
// io.Reader allows a zero-length read; taking &p[0] on an empty slice would
|
||||
// panic, so return early before touching the backing array.
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
content := unsafe.Pointer(&p[0])
|
||||
contentLen := len(p)
|
||||
|
||||
var n C.gsize
|
||||
var gErr *C.GError
|
||||
var res C.gboolean
|
||||
// Reading the WebKit-owned request body stream must happen on the GTK main
|
||||
// loop thread; this runs on a worker goroutine. See issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
res = C.g_input_stream_read_all(r.stream, content, C.gsize(contentLen), &n, nil, &gErr)
|
||||
})
|
||||
if res == 0 {
|
||||
return 0, formatGError("stream read failed", gErr)
|
||||
} else if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
func (r *webkitRequestBody) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
var err error
|
||||
var gErr *C.GError
|
||||
// Closing and unref-ing the WebKit-owned request body stream finalizes a
|
||||
// GObject tied to the GTK main loop; this runs on a worker goroutine, so it
|
||||
// must hop to the main thread. See issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
if C.g_input_stream_close(r.stream, nil, &gErr) == 0 {
|
||||
err = formatGError("stream close failed", gErr)
|
||||
}
|
||||
C.g_object_unref(C.gpointer(r.stream))
|
||||
})
|
||||
r.stream = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func formatGError(msg string, gErr *C.GError, args ...any) error {
|
||||
if gErr != nil && gErr.message != nil {
|
||||
msg += ": " + C.GoString(gErr.message)
|
||||
C.g_error_free(gErr)
|
||||
}
|
||||
return fmt.Errorf(msg, args...)
|
||||
}
|
||||
185
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/webkit_linux_gtk3.go
generated
vendored
Normal file
185
vendor/github.com/wailsapp/wails/v3/internal/assetserver/webview/webkit_linux_gtk3.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
//go:build linux && cgo && gtk3 && !android
|
||||
|
||||
package webview
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1 libsoup-3.0 gio-unix-2.0
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include "libsoup/soup.h"
|
||||
#include "gio/gunixinputstream.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const Webkit2MinMinorVersion = 40
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string {
|
||||
// Reading request metadata touches the WebKit-owned request object, which
|
||||
// belongs to the GTK main loop; this runs on a worker goroutine, so it must
|
||||
// hop to the main thread. See mainthread_linux.go and issue #5631.
|
||||
var method string
|
||||
invokeOnMainSync(func() {
|
||||
method = C.GoString(C.webkit_uri_scheme_request_get_http_method(req))
|
||||
})
|
||||
return strings.ToUpper(method)
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(req *C.WebKitURISchemeRequest) http.Header {
|
||||
h := http.Header{}
|
||||
// Reading and iterating the request's libsoup headers touches WebKit-owned
|
||||
// state on the GTK main loop; this runs on a worker goroutine, so it must hop
|
||||
// to the main thread. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
hdrs := C.webkit_uri_scheme_request_get_http_headers(req)
|
||||
|
||||
var iter C.SoupMessageHeadersIter
|
||||
C.soup_message_headers_iter_init(&iter, hdrs)
|
||||
|
||||
var name *C.char
|
||||
var value *C.char
|
||||
|
||||
for C.soup_message_headers_iter_next(&iter, &name, &value) != 0 {
|
||||
h.Add(C.GoString(name), C.GoString(value))
|
||||
}
|
||||
})
|
||||
return h
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, rFD int, streamLength int64) error {
|
||||
// Completing the request touches WebKit/libsoup objects owned by the GTK
|
||||
// main loop, but this runs on an asset-server worker goroutine. WebKit2GTK
|
||||
// is not thread-safe, so the whole sequence must hop to the main thread.
|
||||
//
|
||||
// The response input stream is created and unref'd inside the same hop: it is
|
||||
// ref-taken by webkit_uri_scheme_response_new on the main thread, so creating
|
||||
// and releasing our reference here too keeps every refcount operation on a
|
||||
// single thread. Previously the stream was built and unref'd on the worker
|
||||
// while WebKit took its ref on the main thread, splitting the stream's
|
||||
// refcount across threads. See mainthread_linux.go and issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
stream := C.g_unix_input_stream_new(C.int(rFD), C.gboolean(1))
|
||||
defer C.g_object_unref(C.gpointer(stream))
|
||||
|
||||
resp := C.webkit_uri_scheme_response_new(stream, C.gint64(streamLength))
|
||||
defer C.g_object_unref(C.gpointer(resp))
|
||||
|
||||
cReason := C.CString(http.StatusText(code))
|
||||
C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason)
|
||||
C.free(unsafe.Pointer(cReason))
|
||||
|
||||
cMimeType := C.CString(header.Get(HeaderContentType))
|
||||
C.webkit_uri_scheme_response_set_content_type(resp, cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
|
||||
// Ownership of hdrs is transferred to the response by
|
||||
// webkit_uri_scheme_response_set_http_headers (transfer full), so we must
|
||||
// not unref it here — doing so frees the headers while WebKit/libsoup still
|
||||
// reference them, crashing in soup_message_headers_iter_next on render.
|
||||
hdrs := C.soup_message_headers_new(C.SOUP_MESSAGE_HEADERS_RESPONSE)
|
||||
for name, values := range header {
|
||||
cName := C.CString(name)
|
||||
for _, value := range values {
|
||||
cValue := C.CString(value)
|
||||
C.soup_message_headers_append(hdrs, cName, cValue)
|
||||
C.free(unsafe.Pointer(cValue))
|
||||
}
|
||||
C.free(unsafe.Pointer(cName))
|
||||
}
|
||||
|
||||
C.webkit_uri_scheme_response_set_http_headers(resp, hdrs)
|
||||
|
||||
C.webkit_uri_scheme_request_finish_with_response(req, resp)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_body(req *C.WebKitURISchemeRequest) io.ReadCloser {
|
||||
// Fetching the request body stream touches the WebKit-owned request on the
|
||||
// GTK main loop; this runs on a worker goroutine, so it must hop to the main
|
||||
// thread. See mainthread_linux.go and issue #5631.
|
||||
var stream *C.GInputStream
|
||||
invokeOnMainSync(func() {
|
||||
stream = C.webkit_uri_scheme_request_get_http_body(req)
|
||||
})
|
||||
if stream == nil {
|
||||
return http.NoBody
|
||||
}
|
||||
return &webkitRequestBody{stream: stream}
|
||||
}
|
||||
|
||||
type webkitRequestBody struct {
|
||||
stream *C.GInputStream
|
||||
closed bool
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *webkitRequestBody) Read(p []byte) (int, error) {
|
||||
if r.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
// io.Reader allows a zero-length read; taking &p[0] on an empty slice would
|
||||
// panic, so return early before touching the backing array.
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
content := unsafe.Pointer(&p[0])
|
||||
contentLen := len(p)
|
||||
|
||||
var n C.gsize
|
||||
var gErr *C.GError
|
||||
var res C.gboolean
|
||||
// Reading the WebKit-owned request body stream must happen on the GTK main
|
||||
// loop thread; this runs on a worker goroutine. See issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
res = C.g_input_stream_read_all(r.stream, content, C.gsize(contentLen), &n, nil, &gErr)
|
||||
})
|
||||
if res == 0 {
|
||||
return 0, formatGError("stream read failed", gErr)
|
||||
} else if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
func (r *webkitRequestBody) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
|
||||
// https://docs.gtk.org/gio/method.InputStream.close.html
|
||||
// Streams will be automatically closed when the last reference is dropped, but you might want to call this function
|
||||
// to make sure resources are released as early as possible.
|
||||
var err error
|
||||
var gErr *C.GError
|
||||
// Closing and unref-ing the WebKit-owned request body stream finalizes a
|
||||
// GObject tied to the GTK main loop; this runs on a worker goroutine, so it
|
||||
// must hop to the main thread. See issue #5631.
|
||||
invokeOnMainSync(func() {
|
||||
if C.g_input_stream_close(r.stream, nil, &gErr) == 0 {
|
||||
err = formatGError("stream close failed", gErr)
|
||||
}
|
||||
C.g_object_unref(C.gpointer(r.stream))
|
||||
})
|
||||
r.stream = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func formatGError(msg string, gErr *C.GError, args ...any) error {
|
||||
if gErr != nil && gErr.message != nil {
|
||||
msg += ": " + C.GoString(gErr.message)
|
||||
C.g_error_free(gErr)
|
||||
}
|
||||
return fmt.Errorf(msg, args...)
|
||||
}
|
||||
12
vendor/github.com/wailsapp/wails/v3/internal/browser/browser.go
generated
vendored
Normal file
12
vendor/github.com/wailsapp/wails/v3/internal/browser/browser.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package browser provides functions to open URLs and files in the default browser.
|
||||
package browser
|
||||
|
||||
// OpenURL opens the named URL in the default browser.
|
||||
func OpenURL(url string) error {
|
||||
return open(url)
|
||||
}
|
||||
|
||||
// OpenFile opens the named file in the default browser or file handler.
|
||||
func OpenFile(path string) error {
|
||||
return open(path)
|
||||
}
|
||||
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_darwin.go
generated
vendored
Normal file
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build darwin
|
||||
|
||||
package browser
|
||||
|
||||
import "os/exec"
|
||||
|
||||
var openCmd = func(target string) *exec.Cmd {
|
||||
return exec.Command("open", target)
|
||||
}
|
||||
|
||||
func open(target string) error {
|
||||
cmd := openCmd(target)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go cmd.Wait() //nolint:errcheck
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_other.go
generated
vendored
Normal file
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_other.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package browser
|
||||
|
||||
import "os/exec"
|
||||
|
||||
var openCmd = func(target string) *exec.Cmd {
|
||||
return exec.Command("xdg-open", target)
|
||||
}
|
||||
|
||||
func open(target string) error {
|
||||
cmd := openCmd(target)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go cmd.Wait() //nolint:errcheck
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_windows.go
generated
vendored
Normal file
18
vendor/github.com/wailsapp/wails/v3/internal/browser/browser_windows.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build windows
|
||||
|
||||
package browser
|
||||
|
||||
import "os/exec"
|
||||
|
||||
var openCmd = func(target string) *exec.Cmd {
|
||||
return exec.Command("rundll32", "url.dll,FileProtocolHandler", target)
|
||||
}
|
||||
|
||||
func open(target string) error {
|
||||
cmd := openCmd(target)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go cmd.Wait() //nolint:errcheck
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities.go
generated
vendored
Normal file
18
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package capabilities
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Capabilities struct {
|
||||
HasNativeDrag bool `json:"HasNativeDrag"`
|
||||
GTKVersion int `json:"GTKVersion"`
|
||||
WebKitVersion string `json:"WebKitVersion"`
|
||||
}
|
||||
|
||||
func (c Capabilities) AsBytes() []byte {
|
||||
// JSON encode
|
||||
result, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return []byte("{}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
9
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_darwin.go
generated
vendored
Normal file
9
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build darwin
|
||||
|
||||
package capabilities
|
||||
|
||||
func newCapabilities(_ string) Capabilities {
|
||||
c := Capabilities{}
|
||||
c.HasNativeDrag = false
|
||||
return c
|
||||
}
|
||||
11
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_linux.go
generated
vendored
Normal file
11
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_linux.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build linux && !gtk3
|
||||
|
||||
package capabilities
|
||||
|
||||
func NewCapabilities() Capabilities {
|
||||
return Capabilities{
|
||||
HasNativeDrag: true,
|
||||
GTKVersion: 4,
|
||||
WebKitVersion: "6.0",
|
||||
}
|
||||
}
|
||||
11
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_linux_gtk3.go
generated
vendored
Normal file
11
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_linux_gtk3.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build linux && gtk3
|
||||
|
||||
package capabilities
|
||||
|
||||
func NewCapabilities() Capabilities {
|
||||
return Capabilities{
|
||||
HasNativeDrag: true,
|
||||
GTKVersion: 3,
|
||||
WebKitVersion: "4.1",
|
||||
}
|
||||
}
|
||||
22
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_windows.go
generated
vendored
Normal file
22
vendor/github.com/wailsapp/wails/v3/internal/capabilities/capabilities_windows.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
//go:build windows
|
||||
|
||||
package capabilities
|
||||
|
||||
import "github.com/wailsapp/wails/v3/internal/webview2/webviewloader"
|
||||
|
||||
type version string
|
||||
|
||||
func (v version) IsAtLeast(input string) bool {
|
||||
result, err := webviewloader.CompareBrowserVersions(string(v), input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return result >= 0
|
||||
}
|
||||
|
||||
func NewCapabilities(webview2version string) Capabilities {
|
||||
webview2 := version(webview2version)
|
||||
c := Capabilities{}
|
||||
c.HasNativeDrag = webview2.IsAtLeast("113.0.0.0")
|
||||
return c
|
||||
}
|
||||
0
vendor/github.com/wailsapp/wails/v3/internal/dbus/menu/.keep
generated
vendored
Normal file
0
vendor/github.com/wailsapp/wails/v3/internal/dbus/menu/.keep
generated
vendored
Normal file
483
vendor/github.com/wailsapp/wails/v3/internal/dbus/menu/dbus_menu.go
generated
vendored
Normal file
483
vendor/github.com/wailsapp/wails/v3/internal/dbus/menu/dbus_menu.go
generated
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
// Code generated by dbus-codegen-go DO NOT EDIT.
|
||||
package menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
)
|
||||
|
||||
var (
|
||||
// Introspection for com.canonical.dbusmenu
|
||||
IntrospectDataDbusmenu = introspect.Interface{
|
||||
Name: "com.canonical.dbusmenu",
|
||||
Methods: []introspect.Method{{Name: "GetLayout", Args: []introspect.Arg{
|
||||
{Name: "parentId", Type: "i", Direction: "in"},
|
||||
{Name: "recursionDepth", Type: "i", Direction: "in"},
|
||||
{Name: "propertyNames", Type: "as", Direction: "in"},
|
||||
{Name: "revision", Type: "u", Direction: "out"},
|
||||
{Name: "layout", Type: "(ia{sv}av)", Direction: "out"},
|
||||
}},
|
||||
{Name: "GetGroupProperties", Args: []introspect.Arg{
|
||||
{Name: "ids", Type: "ai", Direction: "in"},
|
||||
{Name: "propertyNames", Type: "as", Direction: "in"},
|
||||
{Name: "properties", Type: "a(ia{sv})", Direction: "out"},
|
||||
}},
|
||||
{Name: "GetProperty", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "name", Type: "s", Direction: "in"},
|
||||
{Name: "value", Type: "v", Direction: "out"},
|
||||
}},
|
||||
{Name: "Event", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "eventId", Type: "s", Direction: "in"},
|
||||
{Name: "data", Type: "v", Direction: "in"},
|
||||
{Name: "timestamp", Type: "u", Direction: "in"},
|
||||
}},
|
||||
{Name: "EventGroup", Args: []introspect.Arg{
|
||||
{Name: "events", Type: "a(isvu)", Direction: "in"},
|
||||
{Name: "idErrors", Type: "ai", Direction: "out"},
|
||||
}},
|
||||
{Name: "AboutToShow", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "needUpdate", Type: "b", Direction: "out"},
|
||||
}},
|
||||
{Name: "AboutToShowGroup", Args: []introspect.Arg{
|
||||
{Name: "ids", Type: "ai", Direction: "in"},
|
||||
{Name: "updatesNeeded", Type: "ai", Direction: "out"},
|
||||
{Name: "idErrors", Type: "ai", Direction: "out"},
|
||||
}},
|
||||
},
|
||||
Signals: []introspect.Signal{{Name: "ItemsPropertiesUpdated", Args: []introspect.Arg{
|
||||
{Name: "updatedProps", Type: "a(ia{sv})", Direction: "out"},
|
||||
{Name: "removedProps", Type: "a(ias)", Direction: "out"},
|
||||
}},
|
||||
{Name: "LayoutUpdated", Args: []introspect.Arg{
|
||||
{Name: "revision", Type: "u", Direction: "out"},
|
||||
{Name: "parent", Type: "i", Direction: "out"},
|
||||
}},
|
||||
{Name: "ItemActivationRequested", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "out"},
|
||||
{Name: "timestamp", Type: "u", Direction: "out"},
|
||||
}},
|
||||
},
|
||||
Properties: []introspect.Property{{Name: "Version", Type: "u", Access: "read"},
|
||||
{Name: "TextDirection", Type: "s", Access: "read"},
|
||||
{Name: "Status", Type: "s", Access: "read"},
|
||||
{Name: "IconThemePath", Type: "as", Access: "read"},
|
||||
},
|
||||
Annotations: []introspect.Annotation{},
|
||||
}
|
||||
)
|
||||
|
||||
// Signal is a common interface for all signals.
|
||||
type Signal interface {
|
||||
Name() string
|
||||
Interface() string
|
||||
Sender() string
|
||||
|
||||
path() dbus.ObjectPath
|
||||
values() []interface{}
|
||||
}
|
||||
|
||||
// Emit sends the given signal to the bus.
|
||||
func Emit(conn *dbus.Conn, s Signal) error {
|
||||
return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
|
||||
}
|
||||
|
||||
// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
|
||||
var ErrUnknownSignal = errors.New("unknown signal")
|
||||
|
||||
// LookupSignal converts the given raw D-Bus signal with variable body
|
||||
// into one with typed structured body or returns ErrUnknownSignal error.
|
||||
func LookupSignal(signal *dbus.Signal) (Signal, error) {
|
||||
switch signal.Name {
|
||||
case InterfaceDbusmenu + "." + "ItemsPropertiesUpdated":
|
||||
v0, ok := signal.Body[0].([]struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .UpdatedProps is %T, not []struct {V0 int32;V1 map[string]dbus.Variant}", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].([]struct {
|
||||
V0 int32
|
||||
V1 []string
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .RemovedProps is %T, not []struct {V0 int32;V1 []string}", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_ItemsPropertiesUpdatedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_ItemsPropertiesUpdatedSignalBody{
|
||||
UpdatedProps: v0,
|
||||
RemovedProps: v1,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceDbusmenu + "." + "LayoutUpdated":
|
||||
v0, ok := signal.Body[0].(uint32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Revision is %T, not uint32", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].(int32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Parent is %T, not int32", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_LayoutUpdatedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_LayoutUpdatedSignalBody{
|
||||
Revision: v0,
|
||||
Parent: v1,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceDbusmenu + "." + "ItemActivationRequested":
|
||||
v0, ok := signal.Body[0].(int32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Id is %T, not int32", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].(uint32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Timestamp is %T, not uint32", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_ItemActivationRequestedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_ItemActivationRequestedSignalBody{
|
||||
Id: v0,
|
||||
Timestamp: v1,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrUnknownSignal
|
||||
}
|
||||
}
|
||||
|
||||
// AddMatchSignal registers a match rule for the given signal,
|
||||
// opts are appended to the automatically generated signal's rules.
|
||||
func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.AddMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// RemoveMatchSignal unregisters the previously registered subscription.
|
||||
func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.RemoveMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// Interface name constants.
|
||||
const (
|
||||
InterfaceDbusmenu = "com.canonical.dbusmenu"
|
||||
)
|
||||
|
||||
// Dbusmenuer is com.canonical.dbusmenu interface.
|
||||
type Dbusmenuer interface {
|
||||
// GetLayout is com.canonical.dbusmenu.GetLayout method.
|
||||
GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err *dbus.Error)
|
||||
// GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method.
|
||||
GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err *dbus.Error)
|
||||
// GetProperty is com.canonical.dbusmenu.GetProperty method.
|
||||
GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error)
|
||||
// Event is com.canonical.dbusmenu.Event method.
|
||||
Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error)
|
||||
// EventGroup is com.canonical.dbusmenu.EventGroup method.
|
||||
EventGroup(events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err *dbus.Error)
|
||||
// AboutToShow is com.canonical.dbusmenu.AboutToShow method.
|
||||
AboutToShow(id int32) (needUpdate bool, err *dbus.Error)
|
||||
// AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method.
|
||||
AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error)
|
||||
}
|
||||
|
||||
// ExportDbusmenu exports the given object that implements com.canonical.dbusmenu on the bus.
|
||||
func ExportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath, v Dbusmenuer) error {
|
||||
return conn.ExportSubtreeMethodTable(map[string]interface{}{
|
||||
"GetLayout": v.GetLayout,
|
||||
"GetGroupProperties": v.GetGroupProperties,
|
||||
"GetProperty": v.GetProperty,
|
||||
"Event": v.Event,
|
||||
"EventGroup": v.EventGroup,
|
||||
"AboutToShow": v.AboutToShow,
|
||||
"AboutToShowGroup": v.AboutToShowGroup,
|
||||
}, path, InterfaceDbusmenu)
|
||||
}
|
||||
|
||||
// UnexportDbusmenu unexports com.canonical.dbusmenu interface on the named path.
|
||||
func UnexportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath) error {
|
||||
return conn.Export(nil, path, InterfaceDbusmenu)
|
||||
}
|
||||
|
||||
// UnimplementedDbusmenu can be embedded to have forward compatible server implementations.
|
||||
type UnimplementedDbusmenu struct{}
|
||||
|
||||
func (*UnimplementedDbusmenu) iface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) EventGroup(events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
// NewDbusmenu creates and allocates com.canonical.dbusmenu.
|
||||
func NewDbusmenu(object dbus.BusObject) *Dbusmenu {
|
||||
return &Dbusmenu{object}
|
||||
}
|
||||
|
||||
// Dbusmenu implements com.canonical.dbusmenu D-Bus interface.
|
||||
type Dbusmenu struct {
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// GetLayout calls com.canonical.dbusmenu.GetLayout method.
|
||||
func (o *Dbusmenu) GetLayout(ctx context.Context, parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetLayout", 0, parentId, recursionDepth, propertyNames).Store(&revision, &layout)
|
||||
return
|
||||
}
|
||||
|
||||
// GetGroupProperties calls com.canonical.dbusmenu.GetGroupProperties method.
|
||||
func (o *Dbusmenu) GetGroupProperties(ctx context.Context, ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetGroupProperties", 0, ids, propertyNames).Store(&properties)
|
||||
return
|
||||
}
|
||||
|
||||
// GetProperty calls com.canonical.dbusmenu.GetProperty method.
|
||||
func (o *Dbusmenu) GetProperty(ctx context.Context, id int32, name string) (value dbus.Variant, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetProperty", 0, id, name).Store(&value)
|
||||
return
|
||||
}
|
||||
|
||||
// Event calls com.canonical.dbusmenu.Event method.
|
||||
func (o *Dbusmenu) Event(ctx context.Context, id int32, eventId string, data dbus.Variant, timestamp uint32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".Event", 0, id, eventId, data, timestamp).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// EventGroup calls com.canonical.dbusmenu.EventGroup method.
|
||||
func (o *Dbusmenu) EventGroup(ctx context.Context, events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".EventGroup", 0, events).Store(&idErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShow calls com.canonical.dbusmenu.AboutToShow method.
|
||||
func (o *Dbusmenu) AboutToShow(ctx context.Context, id int32) (needUpdate bool, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShow", 0, id).Store(&needUpdate)
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShowGroup calls com.canonical.dbusmenu.AboutToShowGroup method.
|
||||
func (o *Dbusmenu) AboutToShowGroup(ctx context.Context, ids []int32) (updatesNeeded []int32, idErrors []int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShowGroup", 0, ids).Store(&updatesNeeded, &idErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// GetVersion gets com.canonical.dbusmenu.Version property.
|
||||
func (o *Dbusmenu) GetVersion(ctx context.Context) (version uint32, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Version").Store(&version)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTextDirection gets com.canonical.dbusmenu.TextDirection property.
|
||||
func (o *Dbusmenu) GetTextDirection(ctx context.Context) (textDirection string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "TextDirection").Store(&textDirection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetStatus gets com.canonical.dbusmenu.Status property.
|
||||
func (o *Dbusmenu) GetStatus(ctx context.Context) (status string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Status").Store(&status)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconThemePath gets com.canonical.dbusmenu.IconThemePath property.
|
||||
func (o *Dbusmenu) GetIconThemePath(ctx context.Context) (iconThemePath []string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "IconThemePath").Store(&iconThemePath)
|
||||
return
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemsPropertiesUpdatedSignal represents com.canonical.dbusmenu.ItemsPropertiesUpdated signal.
|
||||
type Dbusmenu_ItemsPropertiesUpdatedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_ItemsPropertiesUpdatedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Name() string {
|
||||
return "ItemsPropertiesUpdated"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.UpdatedProps, s.Body.RemovedProps}
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemsPropertiesUpdatedSignalBody is body container.
|
||||
type Dbusmenu_ItemsPropertiesUpdatedSignalBody struct {
|
||||
UpdatedProps []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}
|
||||
RemovedProps []struct {
|
||||
V0 int32
|
||||
V1 []string
|
||||
}
|
||||
}
|
||||
|
||||
// Dbusmenu_LayoutUpdatedSignal represents com.canonical.dbusmenu.LayoutUpdated signal.
|
||||
type Dbusmenu_LayoutUpdatedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_LayoutUpdatedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Name() string {
|
||||
return "LayoutUpdated"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Revision, s.Body.Parent}
|
||||
}
|
||||
|
||||
// Dbusmenu_LayoutUpdatedSignalBody is body container.
|
||||
type Dbusmenu_LayoutUpdatedSignalBody struct {
|
||||
Revision uint32
|
||||
Parent int32
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemActivationRequestedSignal represents com.canonical.dbusmenu.ItemActivationRequested signal.
|
||||
type Dbusmenu_ItemActivationRequestedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_ItemActivationRequestedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Name() string {
|
||||
return "ItemActivationRequested"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Id, s.Body.Timestamp}
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemActivationRequestedSignalBody is body container.
|
||||
type Dbusmenu_ItemActivationRequestedSignalBody struct {
|
||||
Id int32
|
||||
Timestamp uint32
|
||||
}
|
||||
0
vendor/github.com/wailsapp/wails/v3/internal/dbus/notifier/.keep
generated
vendored
Normal file
0
vendor/github.com/wailsapp/wails/v3/internal/dbus/notifier/.keep
generated
vendored
Normal file
636
vendor/github.com/wailsapp/wails/v3/internal/dbus/notifier/status_notifier_item.go
generated
vendored
Normal file
636
vendor/github.com/wailsapp/wails/v3/internal/dbus/notifier/status_notifier_item.go
generated
vendored
Normal file
@@ -0,0 +1,636 @@
|
||||
// Code generated by dbus-codegen-go DO NOT EDIT.
|
||||
package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
)
|
||||
|
||||
var (
|
||||
// Introspection for org.kde.StatusNotifierItem
|
||||
IntrospectDataStatusNotifierItem = introspect.Interface{
|
||||
Name: "org.kde.StatusNotifierItem",
|
||||
Methods: []introspect.Method{{Name: "ContextMenu", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "Activate", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "SecondaryActivate", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "Scroll", Args: []introspect.Arg{
|
||||
{Name: "delta", Type: "i", Direction: "in"},
|
||||
{Name: "orientation", Type: "s", Direction: "in"},
|
||||
}},
|
||||
},
|
||||
Signals: []introspect.Signal{{Name: "NewTitle"},
|
||||
{Name: "NewIcon"},
|
||||
{Name: "NewAttentionIcon"},
|
||||
{Name: "NewOverlayIcon"},
|
||||
{Name: "NewStatus", Args: []introspect.Arg{
|
||||
{Name: "status", Type: "s", Direction: ""},
|
||||
}},
|
||||
{Name: "NewIconThemePath", Args: []introspect.Arg{
|
||||
{Name: "icon_theme_path", Type: "s", Direction: "out"},
|
||||
}},
|
||||
{Name: "NewMenu"},
|
||||
},
|
||||
Properties: []introspect.Property{{Name: "Category", Type: "s", Access: "read"},
|
||||
{Name: "Id", Type: "s", Access: "read"},
|
||||
{Name: "Title", Type: "s", Access: "read"},
|
||||
{Name: "Status", Type: "s", Access: "read"},
|
||||
{Name: "WindowId", Type: "i", Access: "read"},
|
||||
{Name: "IconThemePath", Type: "s", Access: "read"},
|
||||
{Name: "Menu", Type: "o", Access: "read"},
|
||||
{Name: "ItemIsMenu", Type: "b", Access: "read"},
|
||||
{Name: "IconName", Type: "s", Access: "read"},
|
||||
{Name: "IconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "OverlayIconName", Type: "s", Access: "read"},
|
||||
{Name: "OverlayIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "AttentionIconName", Type: "s", Access: "read"},
|
||||
{Name: "AttentionIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "AttentionMovieName", Type: "s", Access: "read"},
|
||||
{Name: "ToolTip", Type: "(sa(iiay)ss)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusToolTipStruct"},
|
||||
}},
|
||||
},
|
||||
Annotations: []introspect.Annotation{},
|
||||
}
|
||||
)
|
||||
|
||||
// Signal is a common interface for all signals.
|
||||
type Signal interface {
|
||||
Name() string
|
||||
Interface() string
|
||||
Sender() string
|
||||
|
||||
path() dbus.ObjectPath
|
||||
values() []interface{}
|
||||
}
|
||||
|
||||
// Emit sends the given signal to the bus.
|
||||
func Emit(conn *dbus.Conn, s Signal) error {
|
||||
return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
|
||||
}
|
||||
|
||||
// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
|
||||
var ErrUnknownSignal = errors.New("unknown signal")
|
||||
|
||||
// LookupSignal converts the given raw D-Bus signal with variable body
|
||||
// into one with typed structured body or returns ErrUnknownSignal error.
|
||||
func LookupSignal(signal *dbus.Signal) (Signal, error) {
|
||||
switch signal.Name {
|
||||
case InterfaceStatusNotifierItem + "." + "NewTitle":
|
||||
return &StatusNotifierItem_NewTitleSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewTitleSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewIcon":
|
||||
return &StatusNotifierItem_NewIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewAttentionIcon":
|
||||
return &StatusNotifierItem_NewAttentionIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewAttentionIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewOverlayIcon":
|
||||
return &StatusNotifierItem_NewOverlayIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewOverlayIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewStatus":
|
||||
v0, ok := signal.Body[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Status is %T, not string", signal.Body[0])
|
||||
}
|
||||
return &StatusNotifierItem_NewStatusSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewStatusSignalBody{
|
||||
Status: v0,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewIconThemePath":
|
||||
v0, ok := signal.Body[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .IconThemePath is %T, not string", signal.Body[0])
|
||||
}
|
||||
return &StatusNotifierItem_NewIconThemePathSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewIconThemePathSignalBody{
|
||||
IconThemePath: v0,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewMenu":
|
||||
return &StatusNotifierItem_NewMenuSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewMenuSignalBody{},
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrUnknownSignal
|
||||
}
|
||||
}
|
||||
|
||||
// AddMatchSignal registers a match rule for the given signal,
|
||||
// opts are appended to the automatically generated signal's rules.
|
||||
func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.AddMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// RemoveMatchSignal unregisters the previously registered subscription.
|
||||
func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.RemoveMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// Interface name constants.
|
||||
const (
|
||||
InterfaceStatusNotifierItem = "org.kde.StatusNotifierItem"
|
||||
)
|
||||
|
||||
// StatusNotifierItemer is org.kde.StatusNotifierItem interface.
|
||||
type StatusNotifierItemer interface {
|
||||
// ContextMenu is org.kde.StatusNotifierItem.ContextMenu method.
|
||||
ContextMenu(x int32, y int32) (err *dbus.Error)
|
||||
// Activate is org.kde.StatusNotifierItem.Activate method.
|
||||
Activate(x int32, y int32) (err *dbus.Error)
|
||||
// SecondaryActivate is org.kde.StatusNotifierItem.SecondaryActivate method.
|
||||
SecondaryActivate(x int32, y int32) (err *dbus.Error)
|
||||
// Scroll is org.kde.StatusNotifierItem.Scroll method.
|
||||
Scroll(delta int32, orientation string) (err *dbus.Error)
|
||||
}
|
||||
|
||||
// ExportStatusNotifierItem exports the given object that implements org.kde.StatusNotifierItem on the bus.
|
||||
func ExportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath, v StatusNotifierItemer) error {
|
||||
return conn.ExportSubtreeMethodTable(map[string]interface{}{
|
||||
"ContextMenu": v.ContextMenu,
|
||||
"Activate": v.Activate,
|
||||
"SecondaryActivate": v.SecondaryActivate,
|
||||
"Scroll": v.Scroll,
|
||||
}, path, InterfaceStatusNotifierItem)
|
||||
}
|
||||
|
||||
// UnexportStatusNotifierItem unexports org.kde.StatusNotifierItem interface on the named path.
|
||||
func UnexportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath) error {
|
||||
return conn.Export(nil, path, InterfaceStatusNotifierItem)
|
||||
}
|
||||
|
||||
// UnimplementedStatusNotifierItem can be embedded to have forward compatible server implementations.
|
||||
type UnimplementedStatusNotifierItem struct{}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) iface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) ContextMenu(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) Activate(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) SecondaryActivate(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) Scroll(delta int32, orientation string) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
// NewStatusNotifierItem creates and allocates org.kde.StatusNotifierItem.
|
||||
func NewStatusNotifierItem(object dbus.BusObject) *StatusNotifierItem {
|
||||
return &StatusNotifierItem{object}
|
||||
}
|
||||
|
||||
// StatusNotifierItem implements org.kde.StatusNotifierItem D-Bus interface.
|
||||
type StatusNotifierItem struct {
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// ContextMenu calls org.kde.StatusNotifierItem.ContextMenu method.
|
||||
func (o *StatusNotifierItem) ContextMenu(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".ContextMenu", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// Activate calls org.kde.StatusNotifierItem.Activate method.
|
||||
func (o *StatusNotifierItem) Activate(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Activate", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// SecondaryActivate calls org.kde.StatusNotifierItem.SecondaryActivate method.
|
||||
func (o *StatusNotifierItem) SecondaryActivate(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".SecondaryActivate", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// Scroll calls org.kde.StatusNotifierItem.Scroll method.
|
||||
func (o *StatusNotifierItem) Scroll(ctx context.Context, delta int32, orientation string) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Scroll", 0, delta, orientation).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// GetCategory gets org.kde.StatusNotifierItem.Category property.
|
||||
func (o *StatusNotifierItem) GetCategory(ctx context.Context) (category string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Category").Store(&category)
|
||||
return
|
||||
}
|
||||
|
||||
// GetId gets org.kde.StatusNotifierItem.Id property.
|
||||
func (o *StatusNotifierItem) GetId(ctx context.Context) (id string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Id").Store(&id)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTitle gets org.kde.StatusNotifierItem.Title property.
|
||||
func (o *StatusNotifierItem) GetTitle(ctx context.Context) (title string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Title").Store(&title)
|
||||
return
|
||||
}
|
||||
|
||||
// GetStatus gets org.kde.StatusNotifierItem.Status property.
|
||||
func (o *StatusNotifierItem) GetStatus(ctx context.Context) (status string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Status").Store(&status)
|
||||
return
|
||||
}
|
||||
|
||||
// GetWindowId gets org.kde.StatusNotifierItem.WindowId property.
|
||||
func (o *StatusNotifierItem) GetWindowId(ctx context.Context) (windowId int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "WindowId").Store(&windowId)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconThemePath gets org.kde.StatusNotifierItem.IconThemePath property.
|
||||
func (o *StatusNotifierItem) GetIconThemePath(ctx context.Context) (iconThemePath string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconThemePath").Store(&iconThemePath)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMenu gets org.kde.StatusNotifierItem.Menu property.
|
||||
func (o *StatusNotifierItem) GetMenu(ctx context.Context) (menu dbus.ObjectPath, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Menu").Store(&menu)
|
||||
return
|
||||
}
|
||||
|
||||
// GetItemIsMenu gets org.kde.StatusNotifierItem.ItemIsMenu property.
|
||||
func (o *StatusNotifierItem) GetItemIsMenu(ctx context.Context) (itemIsMenu bool, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ItemIsMenu").Store(&itemIsMenu)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconName gets org.kde.StatusNotifierItem.IconName property.
|
||||
func (o *StatusNotifierItem) GetIconName(ctx context.Context) (iconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconName").Store(&iconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconPixmap gets org.kde.StatusNotifierItem.IconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
//
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetIconPixmap(ctx context.Context) (iconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconPixmap").Store(&iconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOverlayIconName gets org.kde.StatusNotifierItem.OverlayIconName property.
|
||||
func (o *StatusNotifierItem) GetOverlayIconName(ctx context.Context) (overlayIconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconName").Store(&overlayIconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOverlayIconPixmap gets org.kde.StatusNotifierItem.OverlayIconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
//
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetOverlayIconPixmap(ctx context.Context) (overlayIconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconPixmap").Store(&overlayIconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionIconName gets org.kde.StatusNotifierItem.AttentionIconName property.
|
||||
func (o *StatusNotifierItem) GetAttentionIconName(ctx context.Context) (attentionIconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconName").Store(&attentionIconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionIconPixmap gets org.kde.StatusNotifierItem.AttentionIconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
//
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetAttentionIconPixmap(ctx context.Context) (attentionIconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconPixmap").Store(&attentionIconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionMovieName gets org.kde.StatusNotifierItem.AttentionMovieName property.
|
||||
func (o *StatusNotifierItem) GetAttentionMovieName(ctx context.Context) (attentionMovieName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionMovieName").Store(&attentionMovieName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetToolTip gets org.kde.StatusNotifierItem.ToolTip property.
|
||||
//
|
||||
// Annotations:
|
||||
//
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusToolTipStruct
|
||||
func (o *StatusNotifierItem) GetToolTip(ctx context.Context) (toolTip struct {
|
||||
V0 string
|
||||
V1 []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}
|
||||
V2 string
|
||||
V3 string
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ToolTip").Store(&toolTip)
|
||||
return
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewTitleSignal represents org.kde.StatusNotifierItem.NewTitle signal.
|
||||
type StatusNotifierItem_NewTitleSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewTitleSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Name() string {
|
||||
return "NewTitle"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewTitleSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewTitleSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewTitleSignalBody is body container.
|
||||
type StatusNotifierItem_NewTitleSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconSignal represents org.kde.StatusNotifierItem.NewIcon signal.
|
||||
type StatusNotifierItem_NewIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Name() string {
|
||||
return "NewIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewAttentionIconSignal represents org.kde.StatusNotifierItem.NewAttentionIcon signal.
|
||||
type StatusNotifierItem_NewAttentionIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewAttentionIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Name() string {
|
||||
return "NewAttentionIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewAttentionIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewAttentionIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewOverlayIconSignal represents org.kde.StatusNotifierItem.NewOverlayIcon signal.
|
||||
type StatusNotifierItem_NewOverlayIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewOverlayIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Name() string {
|
||||
return "NewOverlayIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewOverlayIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewOverlayIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewStatusSignal represents org.kde.StatusNotifierItem.NewStatus signal.
|
||||
type StatusNotifierItem_NewStatusSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewStatusSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Name() string {
|
||||
return "NewStatus"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewStatusSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewStatusSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Status}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewStatusSignalBody is body container.
|
||||
type StatusNotifierItem_NewStatusSignalBody struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconThemePathSignal represents org.kde.StatusNotifierItem.NewIconThemePath signal.
|
||||
type StatusNotifierItem_NewIconThemePathSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewIconThemePathSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Name() string {
|
||||
return "NewIconThemePath"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.IconThemePath}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconThemePathSignalBody is body container.
|
||||
type StatusNotifierItem_NewIconThemePathSignalBody struct {
|
||||
IconThemePath string
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewMenuSignal represents org.kde.StatusNotifierItem.NewMenu signal.
|
||||
type StatusNotifierItem_NewMenuSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewMenuSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Name() string {
|
||||
return "NewMenu"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewMenuSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewMenuSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewMenuSignalBody is body container.
|
||||
type StatusNotifierItem_NewMenuSignalBody struct {
|
||||
}
|
||||
41
vendor/github.com/wailsapp/wails/v3/internal/debounce/debounce.go
generated
vendored
Normal file
41
vendor/github.com/wailsapp/wails/v3/internal/debounce/debounce.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package debounce
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// New returns a debounced function that calls f after it stops being invoked
|
||||
// for the given duration. The last invocation wins if called with different functions.
|
||||
func New(after time.Duration) func(f func()) {
|
||||
d := &debouncer{after: after}
|
||||
return func(f func()) {
|
||||
d.add(f)
|
||||
}
|
||||
}
|
||||
|
||||
type debouncer struct {
|
||||
mu sync.Mutex
|
||||
after time.Duration
|
||||
timer *time.Timer
|
||||
generation uint64
|
||||
}
|
||||
|
||||
func (d *debouncer) add(f func()) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.timer != nil {
|
||||
d.timer.Stop()
|
||||
}
|
||||
d.generation++
|
||||
gen := d.generation
|
||||
d.timer = time.AfterFunc(d.after, func() {
|
||||
d.mu.Lock()
|
||||
if d.generation != gen {
|
||||
d.mu.Unlock()
|
||||
return
|
||||
}
|
||||
d.mu.Unlock()
|
||||
f()
|
||||
})
|
||||
}
|
||||
42
vendor/github.com/wailsapp/wails/v3/internal/debug/debug.go
generated
vendored
Normal file
42
vendor/github.com/wailsapp/wails/v3/internal/debug/debug.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var LocalModulePath = ""
|
||||
|
||||
func init() {
|
||||
// Check if .git exists in the relative directory from here: ../../..
|
||||
// If it does, we are in a local build
|
||||
gitDir := RelativePath("..", "..", "..", ".git")
|
||||
if _, err := os.Stat(gitDir); err == nil {
|
||||
modulePath := RelativePath("..", "..", "..")
|
||||
LocalModulePath, _ = filepath.Abs(modulePath)
|
||||
}
|
||||
}
|
||||
|
||||
// RelativePath returns a qualified path created by joining the
|
||||
// directory of the calling file and the given relative path.
|
||||
func RelativePath(relativepath string, optionalpaths ...string) string {
|
||||
_, thisFile, _, _ := runtime.Caller(1)
|
||||
localDir := filepath.Dir(thisFile)
|
||||
|
||||
// If we have optional paths, join them to the relativepath
|
||||
if len(optionalpaths) > 0 {
|
||||
paths := []string{relativepath}
|
||||
paths = append(paths, optionalpaths...)
|
||||
relativepath = filepath.Join(paths...)
|
||||
}
|
||||
result, err := filepath.Abs(filepath.Join(localDir, relativepath))
|
||||
if err != nil {
|
||||
// I'm allowing this for 1 reason only: It's fatal if the path
|
||||
// supplied is wrong as it's only used internally in Wails. If we get
|
||||
// that path wrong, we should know about it immediately. The other reason is
|
||||
// that it cuts down a ton of unnecassary error handling.
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
96
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/desktopfile.go
generated
vendored
Normal file
96
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/desktopfile.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package fileexplorer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DesktopEntry represents a parsed .desktop file's [Desktop Entry] section.
|
||||
// This is a minimal parser that only extracts the fields we need,
|
||||
// replacing the full gopkg.in/ini.v1 dependency (~34KB + 68 transitive deps).
|
||||
type DesktopEntry struct {
|
||||
Exec string
|
||||
}
|
||||
|
||||
// ParseDesktopFile parses a .desktop file and returns the Desktop Entry section.
|
||||
// It follows the Desktop Entry Specification:
|
||||
// ParseDesktopFile parses the `[Desktop Entry]` section of the desktop file at path and returns a DesktopEntry.
|
||||
// It returns an error if the file cannot be opened or if parsing the file fails.
|
||||
func ParseDesktopFile(path string) (*DesktopEntry, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ParseDesktopReader(f)
|
||||
}
|
||||
|
||||
// ParseDesktopReader parses the [Desktop Entry] section of a .desktop file from r and extracts the Exec value.
|
||||
// It ignores empty lines and lines starting with '#', treats section names as case-sensitive, and stops parsing after leaving the [Desktop Entry] section.
|
||||
// The returned *DesktopEntry has Exec set to the exact value of the Exec key if present (whitespace preserved).
|
||||
// An error is returned if reading from r fails.
|
||||
func ParseDesktopReader(r io.Reader) (*DesktopEntry, error) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
entry := &DesktopEntry{}
|
||||
|
||||
inDesktopEntry := false
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Skip empty lines
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip comments (# at start of line)
|
||||
if line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle section headers
|
||||
if line[0] == '[' {
|
||||
// Check if this is the [Desktop Entry] section
|
||||
// The spec says section names are case-sensitive
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "[Desktop Entry]" {
|
||||
inDesktopEntry = true
|
||||
} else if inDesktopEntry {
|
||||
// We've left the [Desktop Entry] section
|
||||
// (e.g., entering [Desktop Action new-window])
|
||||
// We already have what we need, so we can stop
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Only process key=value pairs in [Desktop Entry] section
|
||||
if !inDesktopEntry {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse key=value (spec says no spaces around =, but be lenient)
|
||||
eqIdx := strings.Index(line, "=")
|
||||
if eqIdx == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(line[:eqIdx])
|
||||
value := line[eqIdx+1:] // Don't trim value - preserve intentional whitespace
|
||||
|
||||
// We only need the Exec key
|
||||
// Per spec, keys are case-sensitive and Exec is always "Exec"
|
||||
if key == "Exec" {
|
||||
entry.Exec = value
|
||||
// Continue parsing in case there are multiple Exec lines (shouldn't happen but be safe)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
61
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer.go
generated
vendored
Normal file
61
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package fileexplorer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func OpenFileManager(path string, selectFile bool) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
path = os.ExpandEnv(path)
|
||||
path = filepath.Clean(path)
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve the absolute path: %w", err)
|
||||
}
|
||||
path = absPath
|
||||
if pathInfo, err := os.Stat(path); err != nil {
|
||||
return fmt.Errorf("failed to access the specified path: %w", err)
|
||||
} else {
|
||||
selectFile = selectFile && !pathInfo.IsDir()
|
||||
}
|
||||
|
||||
var (
|
||||
ignoreExitCode bool = false
|
||||
)
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// NOTE: Disabling the exit code check on Windows system. Workaround for explorer.exe
|
||||
// exit code handling (https://github.com/microsoft/WSL/issues/6565)
|
||||
ignoreExitCode = true
|
||||
case "darwin", "linux":
|
||||
default:
|
||||
return errors.New("unsupported platform: " + runtime.GOOS)
|
||||
}
|
||||
|
||||
explorerBin, explorerArgs, err := explorerBinArgs(path, selectFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine the file explorer binary: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, explorerBin, explorerArgs...)
|
||||
cmd.SysProcAttr = sysProcAttr(path, selectFile)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if !ignoreExitCode {
|
||||
return fmt.Errorf("failed to open the file explorer: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
19
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_darwin.go
generated
vendored
Normal file
19
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
//go:build darwin
|
||||
|
||||
package fileexplorer
|
||||
|
||||
import "syscall"
|
||||
|
||||
func explorerBinArgs(path string, selectFile bool) (string, []string, error) {
|
||||
args := []string{}
|
||||
if selectFile {
|
||||
args = append(args, "-R")
|
||||
}
|
||||
|
||||
args = append(args, path)
|
||||
return "open", args, nil
|
||||
}
|
||||
|
||||
func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{}
|
||||
}
|
||||
113
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_linux.go
generated
vendored
Normal file
113
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_linux.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
//go:build linux
|
||||
|
||||
package fileexplorer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// when possible; the fallback method does not support selecting a file.
|
||||
func explorerBinArgs(path string, selectFile bool) (string, []string, error) {
|
||||
// Map of field codes to their replacements
|
||||
var fieldCodes = map[string]string{
|
||||
"%d": "",
|
||||
"%D": "",
|
||||
"%n": "",
|
||||
"%N": "",
|
||||
"%v": "",
|
||||
"%m": "",
|
||||
"%f": path,
|
||||
"%F": path,
|
||||
"%u": pathToURI(path),
|
||||
"%U": pathToURI(path),
|
||||
}
|
||||
fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory")
|
||||
buf := new(bytes.Buffer)
|
||||
fileManagerQuery.Stdout = buf
|
||||
fileManagerQuery.Stderr = nil
|
||||
|
||||
if err := fileManagerQuery.Run(); err != nil {
|
||||
return fallbackExplorerBinArgs(path, selectFile)
|
||||
}
|
||||
|
||||
desktopFilePath, err := findDesktopFile(strings.TrimSpace((buf.String())))
|
||||
if err != nil {
|
||||
return fallbackExplorerBinArgs(path, selectFile)
|
||||
}
|
||||
|
||||
entry, err := ParseDesktopFile(desktopFilePath)
|
||||
if err != nil {
|
||||
// Opting to fallback rather than fail
|
||||
return fallbackExplorerBinArgs(path, selectFile)
|
||||
}
|
||||
|
||||
execCmd := entry.Exec
|
||||
for fieldCode, replacement := range fieldCodes {
|
||||
execCmd = strings.ReplaceAll(execCmd, fieldCode, replacement)
|
||||
}
|
||||
args := strings.Fields(execCmd)
|
||||
if !strings.Contains(strings.Join(args, " "), path) {
|
||||
args = append(args, path)
|
||||
}
|
||||
|
||||
return args[0], args[1:], nil
|
||||
}
|
||||
|
||||
func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{}
|
||||
}
|
||||
|
||||
func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) {
|
||||
// NOTE: The linux fallback explorer opening does not support file selection
|
||||
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", []string{}, fmt.Errorf("stat path: %w", err)
|
||||
}
|
||||
|
||||
// If the path is a file, we want to open the directory containing the file
|
||||
if !stat.IsDir() {
|
||||
path = filepath.Dir(path)
|
||||
}
|
||||
|
||||
return "xdg-open", []string{path}, nil
|
||||
}
|
||||
|
||||
func pathToURI(path string) string {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
// Use url.URL to properly construct file URIs.
|
||||
// url.PathEscape incorrectly escapes forward slashes (/ -> %2F),
|
||||
// which breaks file manager path parsing.
|
||||
u := &url.URL{
|
||||
Scheme: "file",
|
||||
Path: absPath,
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func findDesktopFile(xdgFileName string) (string, error) {
|
||||
paths := []string{
|
||||
filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"),
|
||||
filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"),
|
||||
"/usr/share/applications",
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
desktopFile := filepath.Join(path, xdgFileName)
|
||||
if _, err := os.Stat(desktopFile); err == nil {
|
||||
return desktopFile, nil
|
||||
}
|
||||
}
|
||||
err := fmt.Errorf("desktop file not found: %s", xdgFileName)
|
||||
return "", err
|
||||
}
|
||||
24
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_windows.go
generated
vendored
Normal file
24
vendor/github.com/wailsapp/wails/v3/internal/fileexplorer/fileexplorer_windows.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
//go:build windows
|
||||
|
||||
package fileexplorer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func explorerBinArgs(path string, selectFile bool) (string, []string, error) {
|
||||
return "explorer.exe", []string{}, nil
|
||||
}
|
||||
|
||||
func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr {
|
||||
if selectFile {
|
||||
return &syscall.SysProcAttr{
|
||||
CmdLine: fmt.Sprintf("explorer.exe /select,\"%s\"", path),
|
||||
}
|
||||
} else {
|
||||
return &syscall.SysProcAttr{
|
||||
CmdLine: fmt.Sprintf("explorer.exe \"%s\"", path),
|
||||
}
|
||||
}
|
||||
}
|
||||
91
vendor/github.com/wailsapp/wails/v3/internal/git/git.go
generated
vendored
Normal file
91
vendor/github.com/wailsapp/wails/v3/internal/git/git.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNotInstalled is returned when git is not found in PATH.
|
||||
var ErrNotInstalled = errors.New("git is not installed; please install git from https://git-scm.com")
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
var execErr *exec.Error
|
||||
return errors.As(err, &execErr) && errors.Is(execErr.Err, exec.ErrNotFound)
|
||||
}
|
||||
|
||||
// redactArgs returns a copy of args with any URL credentials (user:pass@host)
|
||||
// replaced by user:***@host so tokens are not leaked in error messages.
|
||||
func redactArgs(args []string) []string {
|
||||
out := make([]string, len(args))
|
||||
for i, a := range args {
|
||||
if u, err := url.Parse(a); err == nil && u.User != nil {
|
||||
u.User = url.UserPassword(u.User.Username(), "***")
|
||||
a = u.String()
|
||||
}
|
||||
out[i] = a
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func run(args ...string) error {
|
||||
out, err := exec.Command("git", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return ErrNotInstalled
|
||||
}
|
||||
return fmt.Errorf("git %s: %w\n%s", strings.Join(redactArgs(args), " "), err, bytes.TrimSpace(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func output(args ...string) (string, error) {
|
||||
out, err := exec.Command("git", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return "", ErrNotInstalled
|
||||
}
|
||||
return "", fmt.Errorf("git %s: %w\n%s", strings.Join(redactArgs(args), " "), err, bytes.TrimSpace(out))
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
// HeadHash returns the short (8-character) commit hash of HEAD in dir.
|
||||
func HeadHash(dir string) (string, error) {
|
||||
hash, err := output("-C", dir, "rev-parse", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(hash) < 8 {
|
||||
return "", fmt.Errorf("git rev-parse returned unexpected output %q", hash)
|
||||
}
|
||||
return hash[:8], nil
|
||||
}
|
||||
|
||||
// Clone clones url into dir. If tag is non-empty, checks out that tag or branch.
|
||||
func Clone(url, dir, tag string) error {
|
||||
args := []string{"clone", "--quiet"}
|
||||
if tag != "" {
|
||||
args = append(args, "--branch", tag)
|
||||
}
|
||||
args = append(args, url, dir)
|
||||
return run(args...)
|
||||
}
|
||||
|
||||
// Init initializes a new git repository at dir.
|
||||
func Init(dir string) error {
|
||||
return run("-C", dir, "init", "--quiet")
|
||||
}
|
||||
|
||||
// RemoteAdd adds a named remote to the repository at dir.
|
||||
func RemoteAdd(dir, name, url string) error {
|
||||
return run("-C", dir, "remote", "add", name, url)
|
||||
}
|
||||
|
||||
// AddAll stages all files in the repository at dir.
|
||||
func AddAll(dir string) error {
|
||||
return run("-C", dir, "add", ".")
|
||||
}
|
||||
21
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/LICENSE
generated
vendored
Normal file
21
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Harry Phillips
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
72
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go
generated
vendored
Normal file
72
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Cross-platform.
|
||||
|
||||
// Common File Dialogs
|
||||
package cfd
|
||||
|
||||
type Dialog interface {
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog.
|
||||
Show() error
|
||||
// Sets the dialog's parent window. Use 0 to set the dialog to have no parent window.
|
||||
SetParentWindowHandle(hwnd uintptr)
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog and returns their selection.
|
||||
// Returns an error if the user cancelled the dialog.
|
||||
// Do not use for the Open Multiple Files dialog. Use ShowAndGetResults instead.
|
||||
ShowAndGetResult() (string, error)
|
||||
// Sets the title of the dialog window.
|
||||
SetTitle(title string) error
|
||||
// Sets the "role" of the dialog. This is used to derive the dialog's GUID, which the
|
||||
// OS will use to differentiate it from dialogs that are intended for other purposes.
|
||||
// This means that, for example, a dialog with role "Import" will have a different
|
||||
// previous location that it will open to than a dialog with role "Open". Can be any string.
|
||||
SetRole(role string) error
|
||||
// Sets the folder used as a default if there is not a recently used folder value available
|
||||
SetDefaultFolder(defaultFolder string) error
|
||||
// Sets the folder that the dialog always opens to.
|
||||
// If this is set, it will override the "default folder" behaviour and the dialog will always open to this folder.
|
||||
SetFolder(folder string) error
|
||||
// Gets the selected file or folder path, as an absolute path eg. "C:\Folder\file.txt"
|
||||
// Do not use for the Open Multiple Files dialog. Use GetResults instead.
|
||||
GetResult() (string, error)
|
||||
// Sets the file name, I.E. the contents of the file name text box.
|
||||
// For Select Folder Dialog, sets folder name.
|
||||
SetFileName(fileName string) error
|
||||
// Release the resources allocated to this Dialog.
|
||||
// Should be called when the dialog is finished with.
|
||||
Release() error
|
||||
}
|
||||
|
||||
type FileDialog interface {
|
||||
Dialog
|
||||
// Set the list of file filters that the user can select.
|
||||
SetFileFilters(fileFilter []FileFilter) error
|
||||
// Set the selected item from the list of file filters (set using SetFileFilters) by its index. Defaults to 0 (the first item in the list) if not called.
|
||||
SetSelectedFileFilterIndex(index uint) error
|
||||
// Sets the default extension applied when a user does not provide one as part of the file name.
|
||||
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
|
||||
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
|
||||
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
|
||||
SetDefaultExtension(defaultExtension string) error
|
||||
}
|
||||
|
||||
type OpenFileDialog interface {
|
||||
FileDialog
|
||||
}
|
||||
|
||||
type OpenMultipleFilesDialog interface {
|
||||
FileDialog
|
||||
// Show the dialog to the user.
|
||||
// Blocks until the user has closed the dialog and returns the selected files.
|
||||
ShowAndGetResults() ([]string, error)
|
||||
// Gets the selected file paths, as absolute paths eg. "C:\Folder\file.txt"
|
||||
GetResults() ([]string, error)
|
||||
}
|
||||
|
||||
type SelectFolderDialog interface {
|
||||
Dialog
|
||||
}
|
||||
|
||||
type SaveFileDialog interface { // TODO Properties
|
||||
FileDialog
|
||||
}
|
||||
28
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_nonWindows.go
generated
vendored
Normal file
28
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_nonWindows.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cfd
|
||||
|
||||
import "fmt"
|
||||
|
||||
var unsupportedError = fmt.Errorf("common file dialogs are only available on windows")
|
||||
|
||||
// TODO doc
|
||||
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
|
||||
return nil, unsupportedError
|
||||
}
|
||||
79
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_windows.go
generated
vendored
Normal file
79
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/CommonFileDialog_windows.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import "github.com/go-ole/go-ole"
|
||||
|
||||
func initialize() {
|
||||
// Swallow error
|
||||
_ = ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE)
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = openDialog.setIsMultiselect(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
|
||||
initialize()
|
||||
|
||||
openDialog, err := newIFileOpenDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(openDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = openDialog.setPickFolders(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openDialog, nil
|
||||
}
|
||||
|
||||
// TODO doc
|
||||
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
|
||||
initialize()
|
||||
|
||||
saveDialog, err := newIFileSaveDialog()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = config.apply(saveDialog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return saveDialog, nil
|
||||
}
|
||||
141
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/DialogConfig.go
generated
vendored
Normal file
141
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/DialogConfig.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// Cross-platform.
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FileFilter struct {
|
||||
// The display name of the filter (That is shown to the user)
|
||||
DisplayName string
|
||||
// The filter pattern. Eg. "*.txt;*.png" to select all txt and png files, "*.*" to select any files, etc.
|
||||
Pattern string
|
||||
}
|
||||
|
||||
// Never obfuscate the FileFilter type.
|
||||
var _ = reflect.TypeOf(FileFilter{})
|
||||
|
||||
type DialogConfig struct {
|
||||
// The title of the dialog
|
||||
Title string
|
||||
// The role of the dialog. This is used to derive the dialog's GUID, which the
|
||||
// OS will use to differentiate it from dialogs that are intended for other purposes.
|
||||
// This means that, for example, a dialog with role "Import" will have a different
|
||||
// previous location that it will open to than a dialog with role "Open". Can be any string.
|
||||
Role string
|
||||
// The default folder - the folder that is used the first time the user opens it
|
||||
// (after the first time their last used location is used).
|
||||
DefaultFolder string
|
||||
// The initial folder - the folder that the dialog always opens to if not empty.
|
||||
// If this is not empty, it will override the "default folder" behaviour and
|
||||
// the dialog will always open to this folder.
|
||||
Folder string
|
||||
// The file filters that restrict which types of files the dialog is able to choose.
|
||||
// Ignored by Select Folder Dialog.
|
||||
FileFilters []FileFilter
|
||||
// Sets the initially selected file filter. This is an index of FileFilters.
|
||||
// Ignored by Select Folder Dialog.
|
||||
SelectedFileFilterIndex uint
|
||||
// The initial name of the file (I.E. the text in the file name text box) when the user opens the dialog.
|
||||
// For the Select Folder Dialog, this sets the initial folder name.
|
||||
FileName string
|
||||
// The default extension applied when a user does not provide one as part of the file name.
|
||||
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
|
||||
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
|
||||
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
|
||||
// Ignored by Select Folder Dialog.
|
||||
DefaultExtension string
|
||||
// ParentWindowHandle is the handle (HWND) to the parent window of the dialog.
|
||||
// If left as 0 / nil, the dialog will have no parent window.
|
||||
ParentWindowHandle uintptr
|
||||
}
|
||||
|
||||
var defaultFilters = []FileFilter{
|
||||
{
|
||||
DisplayName: "All Files (*.*)",
|
||||
Pattern: "*.*",
|
||||
},
|
||||
}
|
||||
|
||||
func (config *DialogConfig) apply(dialog Dialog) (err error) {
|
||||
if config.Title != "" {
|
||||
err = dialog.SetTitle(config.Title)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.Role != "" {
|
||||
err = dialog.SetRole(config.Role)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.Folder != "" {
|
||||
_, err = os.Stat(config.Folder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dialog.SetFolder(config.Folder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.DefaultFolder != "" {
|
||||
_, err = os.Stat(config.DefaultFolder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dialog.SetDefaultFolder(config.DefaultFolder)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.FileName != "" {
|
||||
err = dialog.SetFileName(config.FileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dialog.SetParentWindowHandle(config.ParentWindowHandle)
|
||||
|
||||
if dialog, ok := dialog.(FileDialog); ok {
|
||||
var fileFilters []FileFilter
|
||||
if config.FileFilters != nil && len(config.FileFilters) > 0 {
|
||||
fileFilters = config.FileFilters
|
||||
} else {
|
||||
fileFilters = defaultFilters
|
||||
}
|
||||
err = dialog.SetFileFilters(fileFilters)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config.SelectedFileFilterIndex != 0 {
|
||||
if config.SelectedFileFilterIndex > uint(len(fileFilters)) {
|
||||
err = fmt.Errorf("selected file filter index out of range")
|
||||
return
|
||||
}
|
||||
err = dialog.SetSelectedFileFilterIndex(config.SelectedFileFilterIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if config.DefaultExtension != "" {
|
||||
err = dialog.SetDefaultExtension(config.DefaultExtension)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
7
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/errors.go
generated
vendored
Normal file
7
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/errors.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package cfd
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrorCancelled = errors.New("cancelled by user")
|
||||
)
|
||||
200
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iFileOpenDialog.go
generated
vendored
Normal file
200
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iFileOpenDialog.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/wailsapp/wails/v3/internal/uuid"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
fileOpenDialogCLSID = ole.NewGUID("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}")
|
||||
fileOpenDialogIID = ole.NewGUID("{d57c7288-d4ad-4768-be02-9d969532d960}")
|
||||
)
|
||||
|
||||
type iFileOpenDialog struct {
|
||||
vtbl *iFileOpenDialogVtbl
|
||||
parentWindowHandle uintptr
|
||||
}
|
||||
|
||||
type iFileOpenDialogVtbl struct {
|
||||
iFileDialogVtbl
|
||||
|
||||
GetResults uintptr // func (ppenum **IShellItemArray) HRESULT
|
||||
GetSelectedItems uintptr
|
||||
}
|
||||
|
||||
func newIFileOpenDialog() (*iFileOpenDialog, error) {
|
||||
if unknown, err := ole.CreateInstance(fileOpenDialogCLSID, fileOpenDialogIID); err == nil {
|
||||
return (*iFileOpenDialog)(unsafe.Pointer(unknown)), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) Show() error {
|
||||
return fileOpenDialog.vtbl.show(unsafe.Pointer(fileOpenDialog), fileOpenDialog.parentWindowHandle)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetParentWindowHandle(hwnd uintptr) {
|
||||
fileOpenDialog.parentWindowHandle = hwnd
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResult() (string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use ShowAndGetResults for open multiple files dialog")
|
||||
}
|
||||
if err := fileOpenDialog.Show(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fileOpenDialog.GetResult()
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResults() ([]string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use ShowAndGetResult for open single file dialog")
|
||||
}
|
||||
if err := fileOpenDialog.Show(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileOpenDialog.GetResults()
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetTitle(title string) error {
|
||||
return fileOpenDialog.vtbl.setTitle(unsafe.Pointer(fileOpenDialog), title)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) GetResult() (string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use GetResults for open multiple files dialog")
|
||||
}
|
||||
return fileOpenDialog.vtbl.getResultString(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) Release() error {
|
||||
return fileOpenDialog.vtbl.release(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetDefaultFolder(defaultFolderPath string) error {
|
||||
return fileOpenDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFolder(defaultFolderPath string) error {
|
||||
return fileOpenDialog.vtbl.setFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFileFilters(filter []FileFilter) error {
|
||||
return fileOpenDialog.vtbl.setFileTypes(unsafe.Pointer(fileOpenDialog), filter)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetRole(role string) error {
|
||||
return fileOpenDialog.vtbl.setClientGuid(unsafe.Pointer(fileOpenDialog), StringToUUID(role))
|
||||
}
|
||||
|
||||
// This should only be callable when the user asks for a multi select because
|
||||
// otherwise they will be given the Dialog interface which does not expose this function.
|
||||
func (fileOpenDialog *iFileOpenDialog) GetResults() ([]string, error) {
|
||||
isMultiselect, err := fileOpenDialog.isMultiselect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isMultiselect {
|
||||
// We should panic as this error is caused by the developer using the library
|
||||
panic("use GetResult for open single file dialog")
|
||||
}
|
||||
return fileOpenDialog.vtbl.getResultsStrings(unsafe.Pointer(fileOpenDialog))
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetDefaultExtension(defaultExtension string) error {
|
||||
return fileOpenDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileOpenDialog), defaultExtension)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetFileName(initialFileName string) error {
|
||||
return fileOpenDialog.vtbl.setFileName(unsafe.Pointer(fileOpenDialog), initialFileName)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) SetSelectedFileFilterIndex(index uint) error {
|
||||
return fileOpenDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileOpenDialog), index)
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) setPickFolders(pickFolders bool) error {
|
||||
const FosPickfolders = 0x20
|
||||
if pickFolders {
|
||||
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
|
||||
} else {
|
||||
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
|
||||
}
|
||||
}
|
||||
|
||||
const FosAllowMultiselect = 0x200
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) isMultiselect() (bool, error) {
|
||||
options, err := fileOpenDialog.vtbl.getOptions(unsafe.Pointer(fileOpenDialog))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return options&FosAllowMultiselect != 0, nil
|
||||
}
|
||||
|
||||
func (fileOpenDialog *iFileOpenDialog) setIsMultiselect(isMultiselect bool) error {
|
||||
if isMultiselect {
|
||||
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
|
||||
} else {
|
||||
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileOpenDialogVtbl) getResults(objPtr unsafe.Pointer) (*iShellItemArray, error) {
|
||||
var shellItemArray *iShellItemArray
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetResults,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&shellItemArray)),
|
||||
0)
|
||||
return shellItemArray, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileOpenDialogVtbl) getResultsStrings(objPtr unsafe.Pointer) ([]string, error) {
|
||||
shellItemArray, err := vtbl.getResults(objPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if shellItemArray == nil {
|
||||
return nil, ErrorCancelled
|
||||
}
|
||||
defer shellItemArray.vtbl.release(unsafe.Pointer(shellItemArray))
|
||||
count, err := shellItemArray.vtbl.getCount(unsafe.Pointer(shellItemArray))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var results []string
|
||||
for i := uintptr(0); i < count; i++ {
|
||||
newItem, err := shellItemArray.vtbl.getItemAt(unsafe.Pointer(shellItemArray), i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, newItem)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func StringToUUID(str string) *ole.GUID {
|
||||
return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String())
|
||||
}
|
||||
92
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iFileSaveDialog.go
generated
vendored
Normal file
92
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iFileSaveDialog.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
saveFileDialogCLSID = ole.NewGUID("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
|
||||
saveFileDialogIID = ole.NewGUID("{84bccd23-5fde-4cdb-aea4-af64b83d78ab}")
|
||||
)
|
||||
|
||||
type iFileSaveDialog struct {
|
||||
vtbl *iFileSaveDialogVtbl
|
||||
parentWindowHandle uintptr
|
||||
}
|
||||
|
||||
type iFileSaveDialogVtbl struct {
|
||||
iFileDialogVtbl
|
||||
|
||||
SetSaveAsItem uintptr
|
||||
SetProperties uintptr
|
||||
SetCollectedProperties uintptr
|
||||
GetProperties uintptr
|
||||
ApplyProperties uintptr
|
||||
}
|
||||
|
||||
func newIFileSaveDialog() (*iFileSaveDialog, error) {
|
||||
if unknown, err := ole.CreateInstance(saveFileDialogCLSID, saveFileDialogIID); err == nil {
|
||||
return (*iFileSaveDialog)(unsafe.Pointer(unknown)), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) Show() error {
|
||||
return fileSaveDialog.vtbl.show(unsafe.Pointer(fileSaveDialog), fileSaveDialog.parentWindowHandle)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetParentWindowHandle(hwnd uintptr) {
|
||||
fileSaveDialog.parentWindowHandle = hwnd
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) ShowAndGetResult() (string, error) {
|
||||
if err := fileSaveDialog.Show(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fileSaveDialog.GetResult()
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetTitle(title string) error {
|
||||
return fileSaveDialog.vtbl.setTitle(unsafe.Pointer(fileSaveDialog), title)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) GetResult() (string, error) {
|
||||
return fileSaveDialog.vtbl.getResultString(unsafe.Pointer(fileSaveDialog))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) Release() error {
|
||||
return fileSaveDialog.vtbl.release(unsafe.Pointer(fileSaveDialog))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetDefaultFolder(defaultFolderPath string) error {
|
||||
return fileSaveDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFolder(defaultFolderPath string) error {
|
||||
return fileSaveDialog.vtbl.setFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFileFilters(filter []FileFilter) error {
|
||||
return fileSaveDialog.vtbl.setFileTypes(unsafe.Pointer(fileSaveDialog), filter)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetRole(role string) error {
|
||||
return fileSaveDialog.vtbl.setClientGuid(unsafe.Pointer(fileSaveDialog), StringToUUID(role))
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetDefaultExtension(defaultExtension string) error {
|
||||
return fileSaveDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileSaveDialog), defaultExtension)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetFileName(initialFileName string) error {
|
||||
return fileSaveDialog.vtbl.setFileName(unsafe.Pointer(fileSaveDialog), initialFileName)
|
||||
}
|
||||
|
||||
func (fileSaveDialog *iFileSaveDialog) SetSelectedFileFilterIndex(index uint) error {
|
||||
return fileSaveDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileSaveDialog), index)
|
||||
}
|
||||
56
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iShellItem.go
generated
vendored
Normal file
56
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iShellItem.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procSHCreateItemFromParsingName = syscall.NewLazyDLL("Shell32.dll").NewProc("SHCreateItemFromParsingName")
|
||||
iidShellItem = ole.NewGUID("43826d1e-e718-42ee-bc55-a1e261c37bfe")
|
||||
)
|
||||
|
||||
type iShellItem struct {
|
||||
vtbl *iShellItemVtbl
|
||||
}
|
||||
|
||||
type iShellItemVtbl struct {
|
||||
iUnknownVtbl
|
||||
BindToHandler uintptr
|
||||
GetParent uintptr
|
||||
GetDisplayName uintptr // func (sigdnName SIGDN, ppszName *LPWSTR) HRESULT
|
||||
GetAttributes uintptr
|
||||
Compare uintptr
|
||||
}
|
||||
|
||||
func newIShellItem(path string) (*iShellItem, error) {
|
||||
var shellItem *iShellItem
|
||||
pathPtr := ole.SysAllocString(path)
|
||||
defer func(v *int16) {
|
||||
_ = ole.SysFreeString(v)
|
||||
}(pathPtr)
|
||||
|
||||
ret, _, _ := procSHCreateItemFromParsingName.Call(
|
||||
uintptr(unsafe.Pointer(pathPtr)),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(iidShellItem)),
|
||||
uintptr(unsafe.Pointer(&shellItem)))
|
||||
return shellItem, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemVtbl) getDisplayName(objPtr unsafe.Pointer) (string, error) {
|
||||
var ptr *uint16
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetDisplayName,
|
||||
uintptr(objPtr),
|
||||
0x80058000, // SIGDN_FILESYSPATH,
|
||||
uintptr(unsafe.Pointer(&ptr)))
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer ole.CoTaskMemFree(uintptr(unsafe.Pointer(ptr)))
|
||||
return ole.LpOleStrToString(ptr), nil
|
||||
}
|
||||
65
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iShellItemArray.go
generated
vendored
Normal file
65
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/iShellItemArray.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-ole/go-ole"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
iidShellItemArrayGUID = "{b63ea76d-1f85-456f-a19c-48159efa858b}"
|
||||
)
|
||||
|
||||
var (
|
||||
iidShellItemArray *ole.GUID
|
||||
)
|
||||
|
||||
func init() {
|
||||
iidShellItemArray, _ = ole.IIDFromString(iidShellItemArrayGUID)
|
||||
}
|
||||
|
||||
type iShellItemArray struct {
|
||||
vtbl *iShellItemArrayVtbl
|
||||
}
|
||||
|
||||
type iShellItemArrayVtbl struct {
|
||||
iUnknownVtbl
|
||||
BindToHandler uintptr
|
||||
GetPropertyStore uintptr
|
||||
GetPropertyDescriptionList uintptr
|
||||
GetAttributes uintptr
|
||||
GetCount uintptr // func (pdwNumItems *DWORD) HRESULT
|
||||
GetItemAt uintptr // func (dwIndex DWORD, ppsi **IShellItem) HRESULT
|
||||
EnumItems uintptr
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemArrayVtbl) getCount(objPtr unsafe.Pointer) (uintptr, error) {
|
||||
var count uintptr
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetCount,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&count)))
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (vtbl *iShellItemArrayVtbl) getItemAt(objPtr unsafe.Pointer, index uintptr) (string, error) {
|
||||
var shellItem *iShellItem
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetItemAt,
|
||||
uintptr(objPtr),
|
||||
index,
|
||||
uintptr(unsafe.Pointer(&shellItem)))
|
||||
if err := hresultToError(ret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if shellItem == nil {
|
||||
return "", fmt.Errorf("shellItem is nil")
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
|
||||
}
|
||||
48
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/vtblCommon.go
generated
vendored
Normal file
48
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/vtblCommon.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cfd
|
||||
|
||||
type comDlgFilterSpec struct {
|
||||
pszName *int16
|
||||
pszSpec *int16
|
||||
}
|
||||
|
||||
type iUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
type iModalWindowVtbl struct {
|
||||
iUnknownVtbl
|
||||
Show uintptr // func (hwndOwner HWND) HRESULT
|
||||
}
|
||||
|
||||
type iFileDialogVtbl struct {
|
||||
iModalWindowVtbl
|
||||
SetFileTypes uintptr // func (cFileTypes UINT, rgFilterSpec *COMDLG_FILTERSPEC) HRESULT
|
||||
SetFileTypeIndex uintptr // func(iFileType UINT) HRESULT
|
||||
GetFileTypeIndex uintptr
|
||||
Advise uintptr
|
||||
Unadvise uintptr
|
||||
SetOptions uintptr // func (fos FILEOPENDIALOGOPTIONS) HRESULT
|
||||
GetOptions uintptr // func (pfos *FILEOPENDIALOGOPTIONS) HRESULT
|
||||
SetDefaultFolder uintptr // func (psi *IShellItem) HRESULT
|
||||
SetFolder uintptr // func (psi *IShellItem) HRESULT
|
||||
GetFolder uintptr
|
||||
GetCurrentSelection uintptr
|
||||
SetFileName uintptr // func (pszName LPCWSTR) HRESULT
|
||||
GetFileName uintptr
|
||||
SetTitle uintptr // func(pszTitle LPCWSTR) HRESULT
|
||||
SetOkButtonLabel uintptr
|
||||
SetFileNameLabel uintptr
|
||||
GetResult uintptr // func (ppsi **IShellItem) HRESULT
|
||||
AddPlace uintptr
|
||||
SetDefaultExtension uintptr // func (pszDefaultExtension LPCWSTR) HRESULT
|
||||
// This can only be used from a callback.
|
||||
Close uintptr
|
||||
SetClientGuid uintptr // func (guid REFGUID) HRESULT
|
||||
ClearClientData uintptr
|
||||
SetFilter uintptr
|
||||
}
|
||||
226
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/vtblCommonFunc.go
generated
vendored
Normal file
226
vendor/github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd/vtblCommonFunc.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
//go:build windows
|
||||
|
||||
package cfd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
)
|
||||
|
||||
func hresultToError(hr uintptr) error {
|
||||
if hr < 0 {
|
||||
return ole.NewError(hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vtbl *iUnknownVtbl) release(objPtr unsafe.Pointer) error {
|
||||
ret, _, _ := syscall.SyscallN(vtbl.Release,
|
||||
uintptr(objPtr),
|
||||
0)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iModalWindowVtbl) show(objPtr unsafe.Pointer, hwnd uintptr) error {
|
||||
ret, _, _ := syscall.SyscallN(vtbl.Show,
|
||||
uintptr(objPtr),
|
||||
hwnd)
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFileTypes(objPtr unsafe.Pointer, filters []FileFilter) error {
|
||||
cFileTypes := len(filters)
|
||||
if cFileTypes < 0 {
|
||||
return fmt.Errorf("must specify at least one filter")
|
||||
}
|
||||
comDlgFilterSpecs := make([]comDlgFilterSpec, cFileTypes)
|
||||
for i := 0; i < cFileTypes; i++ {
|
||||
filter := &filters[i]
|
||||
comDlgFilterSpecs[i] = comDlgFilterSpec{
|
||||
pszName: ole.SysAllocString(filter.DisplayName),
|
||||
pszSpec: ole.SysAllocString(filter.Pattern),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure memory is freed after use
|
||||
defer func() {
|
||||
for _, spec := range comDlgFilterSpecs {
|
||||
ole.SysFreeString(spec.pszName)
|
||||
ole.SysFreeString(spec.pszSpec)
|
||||
}
|
||||
}()
|
||||
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetFileTypes,
|
||||
uintptr(objPtr),
|
||||
uintptr(cFileTypes),
|
||||
uintptr(unsafe.Pointer(&comDlgFilterSpecs[0])))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
// Options are:
|
||||
// FOS_OVERWRITEPROMPT = 0x2,
|
||||
// FOS_STRICTFILETYPES = 0x4,
|
||||
// FOS_NOCHANGEDIR = 0x8,
|
||||
// FOS_PICKFOLDERS = 0x20,
|
||||
// FOS_FORCEFILESYSTEM = 0x40,
|
||||
// FOS_ALLNONSTORAGEITEMS = 0x80,
|
||||
// FOS_NOVALIDATE = 0x100,
|
||||
// FOS_ALLOWMULTISELECT = 0x200,
|
||||
// FOS_PATHMUSTEXIST = 0x800,
|
||||
// FOS_FILEMUSTEXIST = 0x1000,
|
||||
// FOS_CREATEPROMPT = 0x2000,
|
||||
// FOS_SHAREAWARE = 0x4000,
|
||||
// FOS_NOREADONLYRETURN = 0x8000,
|
||||
// FOS_NOTESTFILECREATE = 0x10000,
|
||||
// FOS_HIDEMRUPLACES = 0x20000,
|
||||
// FOS_HIDEPINNEDPLACES = 0x40000,
|
||||
// FOS_NODEREFERENCELINKS = 0x100000,
|
||||
// FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
|
||||
// FOS_DONTADDTORECENT = 0x2000000,
|
||||
// FOS_FORCESHOWHIDDEN = 0x10000000,
|
||||
// FOS_DEFAULTNOMINIMODE = 0x20000000,
|
||||
// FOS_FORCEPREVIEWPANEON = 0x40000000,
|
||||
// FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
|
||||
func (vtbl *iFileDialogVtbl) setOptions(objPtr unsafe.Pointer, options uint32) error {
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetOptions,
|
||||
uintptr(objPtr),
|
||||
uintptr(options))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getOptions(objPtr unsafe.Pointer) (uint32, error) {
|
||||
var options uint32
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetOptions,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&options)))
|
||||
return options, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) addOption(objPtr unsafe.Pointer, option uint32) error {
|
||||
if options, err := vtbl.getOptions(objPtr); err == nil {
|
||||
return vtbl.setOptions(objPtr, options|option)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) removeOption(objPtr unsafe.Pointer, option uint32) error {
|
||||
if options, err := vtbl.getOptions(objPtr); err == nil {
|
||||
return vtbl.setOptions(objPtr, options&^option)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setDefaultFolder(objPtr unsafe.Pointer, path string) error {
|
||||
shellItem, err := newIShellItem(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetDefaultFolder,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(shellItem)))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFolder(objPtr unsafe.Pointer, path string) error {
|
||||
shellItem, err := newIShellItem(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetFolder,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(shellItem)))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setTitle(objPtr unsafe.Pointer, title string) error {
|
||||
titlePtr := ole.SysAllocString(title)
|
||||
defer ole.SysFreeString(titlePtr) // Ensure the string is freed
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetTitle,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(titlePtr)))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) close(objPtr unsafe.Pointer) error {
|
||||
ret, _, _ := syscall.SyscallN(vtbl.Close,
|
||||
uintptr(objPtr))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getResult(objPtr unsafe.Pointer) (*iShellItem, error) {
|
||||
var shellItem *iShellItem
|
||||
ret, _, _ := syscall.SyscallN(vtbl.GetResult,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(&shellItem)))
|
||||
return shellItem, hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) getResultString(objPtr unsafe.Pointer) (string, error) {
|
||||
shellItem, err := vtbl.getResult(objPtr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if shellItem == nil {
|
||||
return "", ErrorCancelled
|
||||
}
|
||||
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
|
||||
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setClientGuid(objPtr unsafe.Pointer, guid *ole.GUID) error {
|
||||
// Ensure the GUID is not nil
|
||||
if guid == nil {
|
||||
return fmt.Errorf("guid cannot be nil")
|
||||
}
|
||||
|
||||
// Call the SetClientGuid method
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetClientGuid,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(guid)))
|
||||
|
||||
// Convert the HRESULT to a Go error
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setDefaultExtension(objPtr unsafe.Pointer, defaultExtension string) error {
|
||||
// Ensure the string is not empty before accessing the first character
|
||||
if len(defaultExtension) > 0 && defaultExtension[0] == '.' {
|
||||
defaultExtension = strings.TrimPrefix(defaultExtension, ".")
|
||||
}
|
||||
|
||||
// Allocate memory for the default extension string
|
||||
defaultExtensionPtr := ole.SysAllocString(defaultExtension)
|
||||
defer ole.SysFreeString(defaultExtensionPtr) // Ensure the string is freed
|
||||
|
||||
// Call the SetDefaultExtension method
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetDefaultExtension,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(defaultExtensionPtr)))
|
||||
|
||||
// Convert the HRESULT to a Go error
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setFileName(objPtr unsafe.Pointer, fileName string) error {
|
||||
fileNamePtr := ole.SysAllocString(fileName)
|
||||
defer ole.SysFreeString(fileNamePtr) // Ensure the string is freed
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetFileName,
|
||||
uintptr(objPtr),
|
||||
uintptr(unsafe.Pointer(fileNamePtr)))
|
||||
return hresultToError(ret)
|
||||
}
|
||||
|
||||
func (vtbl *iFileDialogVtbl) setSelectedFileFilterIndex(objPtr unsafe.Pointer, index uint) error {
|
||||
ret, _, _ := syscall.SyscallN(vtbl.SetFileTypeIndex,
|
||||
uintptr(objPtr),
|
||||
uintptr(index+1)) // SetFileTypeIndex counts from 1
|
||||
return hresultToError(ret)
|
||||
}
|
||||
9
vendor/github.com/wailsapp/wails/v3/internal/hash/fnv.go
generated
vendored
Normal file
9
vendor/github.com/wailsapp/wails/v3/internal/hash/fnv.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package hash
|
||||
|
||||
import "hash/fnv"
|
||||
|
||||
func Fnv(s string) uint32 {
|
||||
h := fnv.New32a()
|
||||
_, _ = h.Write([]byte(s)) // Hash implementations never return errors (see https://pkg.go.dev/hash#Hash)
|
||||
return h.Sum32()
|
||||
}
|
||||
86
vendor/github.com/wailsapp/wails/v3/internal/lo/lo.go
generated
vendored
Normal file
86
vendor/github.com/wailsapp/wails/v3/internal/lo/lo.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package lo
|
||||
|
||||
// Associate converts a slice to a map by applying keyFn to each element.
|
||||
func Associate[T any, K comparable, V any](collection []T, keyFn func(T) (K, V)) map[K]V {
|
||||
result := make(map[K]V, len(collection))
|
||||
for _, item := range collection {
|
||||
k, v := keyFn(item)
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Contains reports whether v is present in collection.
|
||||
func Contains[T comparable](collection []T, v T) bool {
|
||||
for _, item := range collection {
|
||||
if item == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsBy reports whether any element of collection satisfies predicate.
|
||||
func ContainsBy[T any](collection []T, predicate func(T) bool) bool {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Find returns the first element satisfying predicate and true, or the zero
|
||||
// value and false if no element matches.
|
||||
func Find[T any](collection []T, predicate func(T) bool) (T, bool) {
|
||||
for _, item := range collection {
|
||||
if predicate(item) {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// Keys returns the keys of the map in an unspecified order.
|
||||
func Keys[K comparable, V any](m map[K]V) []K {
|
||||
result := make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
result = append(result, k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Must returns val and panics if err is non-nil.
|
||||
func Must[T any](val T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Ternary returns ifTrue when condition is true, otherwise ifFalse.
|
||||
func Ternary[T any](condition bool, ifTrue, ifFalse T) T {
|
||||
if condition {
|
||||
return ifTrue
|
||||
}
|
||||
return ifFalse
|
||||
}
|
||||
|
||||
// Without returns a copy of collection with all occurrences of exclude removed.
|
||||
func Without[T comparable](collection []T, exclude ...T) []T {
|
||||
result := make([]T, 0, len(collection))
|
||||
for _, item := range collection {
|
||||
excluded := false
|
||||
for _, ex := range exclude {
|
||||
if item == ex {
|
||||
excluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !excluded {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
23
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os.go
generated
vendored
Normal file
23
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package operatingsystem
|
||||
|
||||
// OS contains information about the operating system
|
||||
type OS struct {
|
||||
ID string `json:"ID"`
|
||||
Name string `json:"Name"`
|
||||
Version string `json:"Version"`
|
||||
Branding string `json:"Branding"`
|
||||
}
|
||||
|
||||
func (o *OS) AsLogSlice() []any {
|
||||
return []any{
|
||||
"ID", o.ID,
|
||||
"Name", o.Name,
|
||||
"Version", o.Version,
|
||||
"Branding", o.Branding,
|
||||
}
|
||||
}
|
||||
|
||||
// Info retrieves information about the current platform
|
||||
func Info() (*OS, error) {
|
||||
return platformInfo()
|
||||
}
|
||||
17
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_android.go
generated
vendored
Normal file
17
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_android.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build android
|
||||
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func platformInfo() (*OS, error) {
|
||||
return &OS{
|
||||
ID: "android",
|
||||
Name: "Android",
|
||||
Version: fmt.Sprintf("Go %s", runtime.Version()),
|
||||
Branding: "Android",
|
||||
}, nil
|
||||
}
|
||||
72
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_darwin.go
generated
vendored
Normal file
72
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
//go:build darwin
|
||||
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var macOSNames = map[string]string{
|
||||
"10.10": "Yosemite",
|
||||
"10.11": "El Capitan",
|
||||
"10.12": "Sierra",
|
||||
"10.13": "High Sierra",
|
||||
"10.14": "Mojave",
|
||||
"10.15": "Catalina",
|
||||
"11": "Big Sur",
|
||||
"12": "Monterey",
|
||||
"13": "Ventura",
|
||||
"14": "Sonoma",
|
||||
"15": "Sequoia",
|
||||
// Add newer versions as they are released...
|
||||
}
|
||||
|
||||
func getOSName(version string) string {
|
||||
trimmedVersion := version
|
||||
if !strings.HasPrefix(version, "10.") {
|
||||
trimmedVersion = strings.SplitN(version, ".", 2)[0]
|
||||
}
|
||||
name, ok := macOSNames[trimmedVersion]
|
||||
if ok {
|
||||
return name
|
||||
}
|
||||
return "MacOS " + version
|
||||
}
|
||||
|
||||
func getSysctlValue(key string) (string, error) {
|
||||
// Run "sysctl" command
|
||||
command := exec.Command("sysctl", key)
|
||||
// Capture stdout
|
||||
var stdout strings.Builder
|
||||
command.Stdout = &stdout
|
||||
// Run command
|
||||
err := command.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version := strings.TrimPrefix(stdout.String(), key+": ")
|
||||
return strings.TrimSpace(version), nil
|
||||
}
|
||||
|
||||
func platformInfo() (*OS, error) {
|
||||
// Default value
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "MacOS"
|
||||
result.Version = "Unknown"
|
||||
|
||||
version, err := getSysctlValue("kern.osproductversion")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Version = version
|
||||
ID, err := getSysctlValue("kern.osversion")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.ID = ID
|
||||
result.Branding = getOSName(result.Version)
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
52
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_linux.go
generated
vendored
Normal file
52
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_linux.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// platformInfo is the platform specific method to get system information
|
||||
func platformInfo() (*OS, error) {
|
||||
_, err := os.Stat("/etc/os-release")
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("unable to read system information")
|
||||
}
|
||||
|
||||
osRelease, _ := os.ReadFile("/etc/os-release")
|
||||
return parseOsRelease(string(osRelease)), nil
|
||||
}
|
||||
|
||||
func parseOsRelease(osRelease string) *OS {
|
||||
|
||||
// Default value
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "Unknown"
|
||||
result.Version = "Unknown"
|
||||
|
||||
// Split into lines
|
||||
lines := strings.Split(osRelease, "\n")
|
||||
// Iterate lines
|
||||
for _, line := range lines {
|
||||
// Split each line by the equals char
|
||||
splitLine := strings.SplitN(line, "=", 2)
|
||||
// Check we have
|
||||
if len(splitLine) != 2 {
|
||||
continue
|
||||
}
|
||||
switch splitLine[0] {
|
||||
case "ID":
|
||||
result.ID = strings.ToLower(strings.Trim(splitLine[1], `"`))
|
||||
case "NAME":
|
||||
result.Name = strings.Trim(splitLine[1], `"`)
|
||||
case "VERSION_ID":
|
||||
result.Version = strings.Trim(splitLine[1], `"`)
|
||||
case "VERSION":
|
||||
result.Branding = strings.Trim(splitLine[1], `"`)
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
34
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_windows.go
generated
vendored
Normal file
34
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/os_windows.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
//go:build windows
|
||||
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v3/pkg/w32"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func platformInfo() (*OS, error) {
|
||||
// Default value
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "Windows"
|
||||
result.Version = "Unknown"
|
||||
|
||||
// Credit: https://stackoverflow.com/a/33288328
|
||||
// Ignore errors as it isn't a showstopper
|
||||
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
|
||||
productName, _, _ := key.GetStringValue("ProductName")
|
||||
currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber")
|
||||
displayVersion, _, _ := key.GetStringValue("DisplayVersion")
|
||||
releaseId, _, _ := key.GetStringValue("ReleaseId")
|
||||
|
||||
result.Name = productName
|
||||
result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild)
|
||||
result.ID = displayVersion
|
||||
result.Branding = w32.GetBranding()
|
||||
|
||||
return &result, key.Close()
|
||||
}
|
||||
62
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/version_windows.go
generated
vendored
Normal file
62
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/version_windows.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
//go:build windows
|
||||
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
type WindowsVersionInfo struct {
|
||||
Major int
|
||||
Minor int
|
||||
Build int
|
||||
DisplayVersion string
|
||||
}
|
||||
|
||||
func (w *WindowsVersionInfo) IsWindowsVersionAtLeast(major, minor, buildNumber int) bool {
|
||||
return w.Major >= major && w.Minor >= minor && w.Build >= buildNumber
|
||||
}
|
||||
|
||||
func GetWindowsVersionInfo() (*WindowsVersionInfo, error) {
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &WindowsVersionInfo{
|
||||
Major: regDWORDKeyAsInt(key, "CurrentMajorVersionNumber"),
|
||||
Minor: regDWORDKeyAsInt(key, "CurrentMinorVersionNumber"),
|
||||
Build: regStringKeyAsInt(key, "CurrentBuildNumber"),
|
||||
DisplayVersion: regKeyAsString(key, "DisplayVersion"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func regDWORDKeyAsInt(key registry.Key, name string) int {
|
||||
result, _, err := key.GetIntegerValue(name)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(result)
|
||||
}
|
||||
|
||||
func regStringKeyAsInt(key registry.Key, name string) int {
|
||||
resultStr, _, err := key.GetStringValue(name)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
result, err := strconv.Atoi(resultStr)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func regKeyAsString(key registry.Key, name string) string {
|
||||
resultStr, _, err := key.GetStringValue(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return resultStr
|
||||
}
|
||||
42
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/webkit_linux.go
generated
vendored
Normal file
42
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/webkit_linux.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
//go:build linux && cgo && !gtk3 && !android
|
||||
|
||||
package operatingsystem
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk4 webkitgtk-6.0
|
||||
#include <webkit/webkit.h>
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
type WebkitVersion struct {
|
||||
Major uint
|
||||
Minor uint
|
||||
Micro uint
|
||||
}
|
||||
|
||||
func GetWebkitVersion() WebkitVersion {
|
||||
var major, minor, micro C.uint
|
||||
major = C.webkit_get_major_version()
|
||||
minor = C.webkit_get_minor_version()
|
||||
micro = C.webkit_get_micro_version()
|
||||
return WebkitVersion{
|
||||
Major: uint(major),
|
||||
Minor: uint(minor),
|
||||
Micro: uint(micro),
|
||||
}
|
||||
}
|
||||
|
||||
func (v WebkitVersion) String() string {
|
||||
return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Micro)
|
||||
}
|
||||
|
||||
func (v WebkitVersion) IsAtLeast(major int, minor int, micro int) bool {
|
||||
if v.Major != uint(major) {
|
||||
return v.Major > uint(major)
|
||||
}
|
||||
if v.Minor != uint(minor) {
|
||||
return v.Minor > uint(minor)
|
||||
}
|
||||
return v.Micro >= uint(micro)
|
||||
}
|
||||
42
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/webkit_linux_gtk3.go
generated
vendored
Normal file
42
vendor/github.com/wailsapp/wails/v3/internal/operatingsystem/webkit_linux_gtk3.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
//go:build linux && cgo && gtk3 && !android
|
||||
|
||||
package operatingsystem
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1
|
||||
#include <webkit2/webkit2.h>
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
type WebkitVersion struct {
|
||||
Major uint
|
||||
Minor uint
|
||||
Micro uint
|
||||
}
|
||||
|
||||
func GetWebkitVersion() WebkitVersion {
|
||||
var major, minor, micro C.uint
|
||||
major = C.webkit_get_major_version()
|
||||
minor = C.webkit_get_minor_version()
|
||||
micro = C.webkit_get_micro_version()
|
||||
return WebkitVersion{
|
||||
Major: uint(major),
|
||||
Minor: uint(minor),
|
||||
Micro: uint(micro),
|
||||
}
|
||||
}
|
||||
|
||||
func (v WebkitVersion) String() string {
|
||||
return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Micro)
|
||||
}
|
||||
|
||||
func (v WebkitVersion) IsAtLeast(major int, minor int, micro int) bool {
|
||||
if v.Major != uint(major) {
|
||||
return v.Major > uint(major)
|
||||
}
|
||||
if v.Minor != uint(minor) {
|
||||
return v.Minor > uint(minor)
|
||||
}
|
||||
return v.Micro >= uint(micro)
|
||||
}
|
||||
49
vendor/github.com/wailsapp/wails/v3/internal/optional/optional.go
generated
vendored
Normal file
49
vendor/github.com/wailsapp/wails/v3/internal/optional/optional.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package optional
|
||||
|
||||
// True is a Bool set to true.
|
||||
var True = NewBool(true)
|
||||
|
||||
// False is a Bool set to false.
|
||||
var False = NewBool(false)
|
||||
|
||||
// Bool is an optional bool value.
|
||||
type Bool = Var[bool]
|
||||
|
||||
// NewBool creates a new Bool with the given value.
|
||||
func NewBool(val bool) Bool {
|
||||
return NewVar(val)
|
||||
}
|
||||
|
||||
// Var is a generic optional value that tracks whether it has been set.
|
||||
type Var[T any] struct {
|
||||
val T
|
||||
set bool
|
||||
}
|
||||
|
||||
// Get returns the value, or the zero value if unset.
|
||||
func (v *Var[T]) Get() T {
|
||||
return v.val
|
||||
}
|
||||
|
||||
// Set assigns a value and marks the variable as set.
|
||||
func (v *Var[T]) Set(val T) {
|
||||
v.val = val
|
||||
v.set = true
|
||||
}
|
||||
|
||||
// IsSet reports whether a value has been assigned.
|
||||
func (v *Var[T]) IsSet() bool {
|
||||
return v.set
|
||||
}
|
||||
|
||||
// Unset resets the variable to the zero value and marks it as unset.
|
||||
func (v *Var[T]) Unset() {
|
||||
v.set = false
|
||||
var zero T
|
||||
v.val = zero
|
||||
}
|
||||
|
||||
// NewVar creates a new Var with the given value, marked as set.
|
||||
func NewVar[T any](val T) Var[T] {
|
||||
return Var[T]{val: val, set: true}
|
||||
}
|
||||
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/.gitignore
generated
vendored
Normal file
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
.task
|
||||
*.tsbuildinfo
|
||||
desktop/@wailsio/runtime/dist/
|
||||
desktop/@wailsio/runtime/types/
|
||||
3
vendor/github.com/wailsapp/wails/v3/internal/runtime/README.md
generated
vendored
Normal file
3
vendor/github.com/wailsapp/wails/v3/internal/runtime/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Runtime
|
||||
|
||||
To rebuild the runtime run `task build` or if you have Wails v3 CLI, you can use `wails3 task build`.
|
||||
128
vendor/github.com/wailsapp/wails/v3/internal/runtime/Taskfile.yaml
generated
vendored
Normal file
128
vendor/github.com/wailsapp/wails/v3/internal/runtime/Taskfile.yaml
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: "3"
|
||||
|
||||
vars:
|
||||
ESBUILD: desktop/@wailsio/runtime/node_modules/.bin/esbuild
|
||||
|
||||
tasks:
|
||||
install-deps:
|
||||
internal: true
|
||||
# once: build:debug and build:production dep on this in parallel; two
|
||||
# concurrent npm installs in the same directory corrupt node_modules.
|
||||
run: once
|
||||
dir: desktop/@wailsio/runtime
|
||||
sources:
|
||||
- package.json
|
||||
cmds:
|
||||
- npm install
|
||||
|
||||
check:
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm run check
|
||||
|
||||
test:
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm test
|
||||
|
||||
build:debug:
|
||||
internal: true
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- "{{.ESBUILD}} desktop/@wailsio/runtime/src/index.ts --inject:desktop/compiled/main.js --format=esm --target=safari11 --bundle --ignore-annotations --tree-shaking=true --sourcemap=inline --outfile=../assetserver/bundledassets/runtime.debug.js --define:DEBUG=true"
|
||||
|
||||
build:production:
|
||||
internal: true
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- "{{.ESBUILD}} desktop/@wailsio/runtime/src/index.ts --inject:desktop/compiled/main.js --format=esm --target=safari11 --bundle --ignore-annotations --tree-shaking=true --minify --outfile=../assetserver/bundledassets/runtime.js --define:DEBUG=false --drop:console"
|
||||
|
||||
build:docs:
|
||||
internal: true
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm run build:docs
|
||||
|
||||
build:docs:md:
|
||||
internal: true
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm run build:docs:md
|
||||
|
||||
build:assets:
|
||||
desc: Rebuild only the embedded runtime bundles (bundledassets); CI verifies PR bundles against this exact output.
|
||||
deps:
|
||||
- build:debug
|
||||
- build:production
|
||||
|
||||
build:runtime:
|
||||
internal: true
|
||||
deps:
|
||||
- build:debug
|
||||
- build:production
|
||||
|
||||
cmds:
|
||||
- cmd: echo "Runtime build complete."
|
||||
|
||||
build:all:
|
||||
internal: true
|
||||
cmds:
|
||||
- task: generate:events
|
||||
- task: build:docs
|
||||
- task: build:runtime
|
||||
- echo "Build Complete."
|
||||
|
||||
build:
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- task: build:all
|
||||
|
||||
docs:
|
||||
summary: Generate TypeDoc documentation for the runtime
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm run build:docs
|
||||
- echo "Documentation generated at desktop/@wailsio/runtime/docs/"
|
||||
|
||||
docs:md:
|
||||
summary: Generate markdown documentation for the runtime
|
||||
dir: desktop/@wailsio/runtime
|
||||
deps:
|
||||
- install-deps
|
||||
cmds:
|
||||
- npm run build:docs:md
|
||||
- echo "Markdown documentation generated"
|
||||
|
||||
generate:events:
|
||||
dir: ../../tasks/events
|
||||
cmds:
|
||||
- go run generate.go
|
||||
- go fmt ../../pkg/events/events.go
|
||||
|
||||
clean:
|
||||
summary: Clean built artifacts and documentation
|
||||
dir: desktop/@wailsio/runtime
|
||||
cmds:
|
||||
- npm run clean
|
||||
- echo "Cleaned runtime artifacts"
|
||||
|
||||
generate:
|
||||
summary: Generate events only (use runtime:build to rebuild everything)
|
||||
cmds:
|
||||
- task: generate:events
|
||||
- echo "Events generated. Run 'wails3 task runtime:build' to rebuild runtime with updated documentation"
|
||||
6
vendor/github.com/wailsapp/wails/v3/internal/runtime/package-lock.json
generated
vendored
Normal file
6
vendor/github.com/wailsapp/wails/v3/internal/runtime/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "runtime",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
22
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime.go
generated
vendored
Normal file
22
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var runtimeInit = `window._wails=window._wails||{};window._wails.flags=window._wails.flags||{};window.wails=window.wails||{};`
|
||||
var runtimeConfigReady = `Promise.resolve().then(function(){window.dispatchEvent(new Event("wails:runtime-config-ready"));});`
|
||||
|
||||
func Core(flags map[string]any) string {
|
||||
flagsStr := ""
|
||||
if len(flags) > 0 {
|
||||
f, err := json.Marshal(flags)
|
||||
if err == nil {
|
||||
flagsStr += fmt.Sprintf("window._wails.flags=%s;", f)
|
||||
}
|
||||
}
|
||||
|
||||
return runtimeInit + flagsStr + invoke + environment + runtimeConfigReady
|
||||
}
|
||||
16
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_android.go
generated
vendored
Normal file
16
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_android.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build android
|
||||
|
||||
package runtime
|
||||
|
||||
// Android uses window.wails.invoke which is set up via addJavascriptInterface in WailsJSBridge
|
||||
// We need to log the state to debug why it's not being detected
|
||||
var invoke = `
|
||||
console.log('[Wails Android Runtime] Injecting runtime, window.wails exists:', !!window.wails);
|
||||
console.log('[Wails Android Runtime] window.wails.invoke exists:', !!(window.wails && window.wails.invoke));
|
||||
window._wails.invoke=function(m){
|
||||
console.log('[Wails Android Runtime] _wails.invoke called:', m);
|
||||
return window.wails.invoke(typeof m==='string'?m:JSON.stringify(m));
|
||||
};
|
||||
console.log('[Wails Android Runtime] Runtime injection complete');
|
||||
`
|
||||
var flags = ""
|
||||
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_darwin.go
generated
vendored
Normal file
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build darwin
|
||||
|
||||
package runtime
|
||||
|
||||
var invoke = "window._wails.invoke=function(msg){window.webkit.messageHandlers.external.postMessage(msg);};"
|
||||
10
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_dev.go
generated
vendored
Normal file
10
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_dev.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build !production
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var environment = fmt.Sprintf(`window._wails.environment={"OS":"%s","Arch":"%s","Debug":true};`, runtime.GOOS, runtime.GOARCH)
|
||||
11
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_linux.go
generated
vendored
Normal file
11
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_linux.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package runtime
|
||||
|
||||
// On webkit2gtk, `messageHandlers.external.postMessage` only works when
|
||||
// `this` is bound to the handler object. Assigning the bare function
|
||||
// reference (as we did historically) silently swallows messages when
|
||||
// called as `window._wails.invoke(msg)` — the page's invoke loses the
|
||||
// receiver. Wrap it like darwin does so callers can invoke without
|
||||
// thinking about receiver binding.
|
||||
var invoke = "window._wails.invoke=function(msg){window.webkit.messageHandlers.external.postMessage(msg);};"
|
||||
8
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_prod.go
generated
vendored
Normal file
8
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_prod.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build production
|
||||
|
||||
package runtime
|
||||
|
||||
import "fmt"
|
||||
import goruntime "runtime"
|
||||
|
||||
var environment = fmt.Sprintf(`window._wails.environment={"OS":"%s","Arch":"%s","Debug":false};`, goruntime.GOOS, goruntime.GOARCH)
|
||||
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_windows.go
generated
vendored
Normal file
5
vendor/github.com/wailsapp/wails/v3/internal/runtime/runtime_windows.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build windows
|
||||
|
||||
package runtime
|
||||
|
||||
var invoke = `window._wails.invoke=window.chrome.webview.postMessage;`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user