mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 00:09:56 +08:00
[linux] Add support for WebKit2GTK 2.36+ features (#2151)
This commit is contained in:
parent
7f8952ebe5
commit
5418b879c4
@ -126,15 +126,17 @@ func (f *Frontend) WindowClose() {
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
|
||||
|
||||
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
|
||||
if os.Getenv("GDK_BACKEND") == "" && (os.Getenv("XDG_SESSION_TYPE") == "" || os.Getenv("XDG_SESSION_TYPE") == "unspecified" || os.Getenv("XDG_SESSION_TYPE") == "x11") {
|
||||
_ = os.Setenv("GDK_BACKEND", "x11")
|
||||
}
|
||||
|
||||
C.gtk_init(nil, nil)
|
||||
}
|
||||
|
||||
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
|
||||
|
||||
result := &Frontend{
|
||||
frontendOptions: appoptions,
|
||||
logger: myLogger,
|
||||
@ -171,8 +173,6 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
|
||||
go result.startMessageProcessor()
|
||||
|
||||
C.gtk_init(nil, nil)
|
||||
|
||||
var _debug = ctx.Value("debug")
|
||||
if _debug != nil {
|
||||
result.debug = _debug.(bool)
|
||||
@ -480,8 +480,6 @@ func (f *Frontend) processRequest(request unsafe.Pointer) {
|
||||
uri := C.webkit_uri_scheme_request_get_uri(req)
|
||||
goURI := C.GoString(uri)
|
||||
|
||||
// WebKitGTK stable < 2.36 API does not support request method, request headers and request.
|
||||
// Apart from request bodies, this is only available beginning with 2.36: https://webkitgtk.org/reference/webkit2gtk/stable/WebKitURISchemeResponse.html
|
||||
rw := &webKitResponseWriter{req: req}
|
||||
defer rw.Close()
|
||||
|
||||
@ -489,20 +487,22 @@ func (f *Frontend) processRequest(request unsafe.Pointer) {
|
||||
goURI,
|
||||
rw,
|
||||
func() (*http.Request, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, goURI, nil)
|
||||
method := webkit_uri_scheme_request_get_http_method(req)
|
||||
r, err := http.NewRequest(method, goURI, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Header = webkit_uri_scheme_request_get_http_headers(req)
|
||||
|
||||
if req.URL.Host != f.startURL.Host {
|
||||
if req.Body != nil {
|
||||
req.Body.Close()
|
||||
if r.URL.Host != f.startURL.Host {
|
||||
if r.Body != nil {
|
||||
r.Body.Close()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Expected host '%s' in request, but was '%s'", f.startURL.Host, req.URL.Host)
|
||||
return nil, fmt.Errorf("Expected host '%s' in request, but was '%s'", f.startURL.Host, r.URL.Host)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
return r, nil
|
||||
})
|
||||
|
||||
}
|
||||
|
29
v2/internal/frontend/desktop/linux/webkit2.go
Normal file
29
v2/internal/frontend/desktop/linux/webkit2.go
Normal file
@ -0,0 +1,29 @@
|
||||
//go:build linux
|
||||
|
||||
package linux
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: webkit2gtk-4.0
|
||||
#include "webkit2/webkit2.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/linux"
|
||||
)
|
||||
|
||||
func validateWebKit2Version(options *options.App) {
|
||||
if C.webkit_get_major_version() == 2 && C.webkit_get_minor_version() >= webkit2MinMinorVersion {
|
||||
return
|
||||
}
|
||||
|
||||
msg := linux.DefaultMessages()
|
||||
if options.Linux != nil && options.Linux.Messages != nil {
|
||||
msg = options.Linux.Messages
|
||||
}
|
||||
|
||||
v := fmt.Sprintf("2.%d.0", webkit2MinMinorVersion)
|
||||
showModalDialogAndExit("WebKit2GTK", fmt.Sprintf(msg.WebKit2GTKMinRequired, v))
|
||||
}
|
73
v2/internal/frontend/desktop/linux/webkit2_36.go
Normal file
73
v2/internal/frontend/desktop/linux/webkit2_36.go
Normal file
@ -0,0 +1,73 @@
|
||||
//go:build linux && webkit2_36
|
||||
|
||||
package linux
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 libsoup-2.4
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include "libsoup/soup.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
)
|
||||
|
||||
const webkit2MinMinorVersion = 36
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string {
|
||||
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 {
|
||||
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
|
||||
|
||||
h := http.Header{}
|
||||
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, stream *C.GInputStream, streamLength int64) error {
|
||||
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(assetserver.HeaderContentType))
|
||||
C.webkit_uri_scheme_response_set_content_type(resp, cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
|
||||
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
|
||||
}
|
40
v2/internal/frontend/desktop/linux/webkit2_legacy.go
Normal file
40
v2/internal/frontend/desktop/linux/webkit2_legacy.go
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build linux && !webkit2_36
|
||||
|
||||
package linux
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
)
|
||||
|
||||
const webkit2MinMinorVersion = 0
|
||||
|
||||
func webkit_uri_scheme_request_get_http_method(_ *C.WebKitURISchemeRequest) string {
|
||||
return http.MethodGet
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_get_http_headers(_ *C.WebKitURISchemeRequest) http.Header {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, header http.Header, stream *C.GInputStream, streamLength int64) error {
|
||||
if code != http.StatusOK {
|
||||
return fmt.Errorf("StatusCodes not supported: %d - %s", code, http.StatusText(code))
|
||||
}
|
||||
|
||||
cMimeType := C.CString(header.Get(assetserver.HeaderContentType))
|
||||
C.webkit_uri_scheme_request_finish(req, stream, C.gint64(streamLength), cMimeType)
|
||||
C.free(unsafe.Pointer(cMimeType))
|
||||
return nil
|
||||
}
|
@ -55,27 +55,6 @@ func (rw *webKitResponseWriter) WriteHeader(code int) {
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
if code != http.StatusOK {
|
||||
// WebKitGTK stable < 2.36 API does not support response headers and response statuscodes
|
||||
rw.w = &nopCloser{io.Discard}
|
||||
rw.finishWithError(http.StatusText(code), code)
|
||||
return
|
||||
}
|
||||
|
||||
// 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.wErr = fmt.Errorf("Unable opening pipe: %s", err)
|
||||
rw.finishWithError(rw.wErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rw.w = w
|
||||
|
||||
cMimeType := C.CString(rw.Header().Get(assetserver.HeaderContentType))
|
||||
defer C.free(unsafe.Pointer(cMimeType))
|
||||
|
||||
contentLength := int64(-1)
|
||||
if sLen := rw.Header().Get(assetserver.HeaderContentLength); sLen != "" {
|
||||
if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 {
|
||||
@ -83,9 +62,23 @@ func (rw *webKitResponseWriter) WriteHeader(code int) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
stream := C.g_unix_input_stream_new(C.int(rFD), gtkBool(true))
|
||||
C.webkit_uri_scheme_request_finish(rw.req, stream, C.gint64(contentLength), cMimeType)
|
||||
C.g_object_unref(C.gpointer(stream))
|
||||
defer C.g_object_unref(C.gpointer(stream))
|
||||
|
||||
if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), stream, contentLength); err != nil {
|
||||
rw.finishWithError(http.StatusInternalServerError, fmt.Errorf("unable to finish request: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *webKitResponseWriter) Close() {
|
||||
@ -94,8 +87,14 @@ func (rw *webKitResponseWriter) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *webKitResponseWriter) finishWithError(message string, code int) {
|
||||
msg := C.CString(http.StatusText(code))
|
||||
func (rw *webKitResponseWriter) 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())
|
||||
gerr := C.g_error_new_literal(C.g_quark_from_string(msg), C.int(code), msg)
|
||||
C.webkit_uri_scheme_request_finish_error(rw.req, gerr)
|
||||
C.g_error_free(gerr)
|
@ -642,6 +642,7 @@ static void SetWindowTransparency(GtkWidget *widget)
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
@ -679,6 +680,7 @@ func bool2Cint(value bool) C.int {
|
||||
}
|
||||
|
||||
func NewWindow(appoptions *options.App, debug bool) *Window {
|
||||
validateWebKit2Version(appoptions)
|
||||
|
||||
result := &Window{
|
||||
appoptions: appoptions,
|
||||
@ -1034,3 +1036,19 @@ func (w *Window) ToggleMaximise() {
|
||||
w.Maximise()
|
||||
}
|
||||
}
|
||||
|
||||
// showModalDialogAndExit shows a modal dialog and exits the app.
|
||||
func showModalDialogAndExit(title, message string) {
|
||||
go func() {
|
||||
data := C.MessageDialogOptions{
|
||||
title: C.CString(title),
|
||||
message: C.CString(message),
|
||||
messageType: C.int(1),
|
||||
}
|
||||
|
||||
C.messageDialog(unsafe.Pointer(&data))
|
||||
}()
|
||||
|
||||
<-messageDialogResult
|
||||
log.Fatal(message)
|
||||
}
|
||||
|
@ -4,4 +4,17 @@ package linux
|
||||
type Options struct {
|
||||
Icon []byte
|
||||
WindowIsTranslucent bool
|
||||
|
||||
// User messages that can be customised
|
||||
Messages *Messages
|
||||
}
|
||||
|
||||
type Messages struct {
|
||||
WebKit2GTKMinRequired string
|
||||
}
|
||||
|
||||
func DefaultMessages() *Messages {
|
||||
return &Messages{
|
||||
WebKit2GTKMinRequired: "This application requires at least WebKit2GTK %s to be installed.",
|
||||
}
|
||||
}
|
||||
|
@ -250,23 +250,23 @@ dynamically with an `http.Handler` or hook into the request chain with an `asset
|
||||
Not all features of an `http.Request` are currently supported, please see the following feature matrix:
|
||||
|
||||
| Feature | Win | Mac | Lin |
|
||||
| ----------------------- | --- | --- | --- |
|
||||
| ----------------------- | --- | --- | ------ |
|
||||
| GET | ✅ | ✅ | ✅ |
|
||||
| POST | ✅ | ✅ | ❌ |
|
||||
| PUT | ✅ | ✅ | ❌ |
|
||||
| PATCH | ✅ | ✅ | ❌ |
|
||||
| DELETE | ✅ | ✅ | ❌ |
|
||||
| Request Headers | ✅ | ✅ | ❌ |
|
||||
| POST | ✅ | ✅ | ✅ [^1] |
|
||||
| PUT | ✅ | ✅ | ✅ [^1] |
|
||||
| PATCH | ✅ | ✅ | ✅ [^1] |
|
||||
| DELETE | ✅ | ✅ | ✅ [^1] |
|
||||
| Request Headers | ✅ | ✅ | ✅ [^1] |
|
||||
| Request Body | ✅ | ✅ | ❌ |
|
||||
| Request Body Streaming | ❌ | ❌ | ❌ |
|
||||
| Response StatusCodes | ✅ | ✅ | ❌ |
|
||||
| Response Headers | ✅ | ✅ | ❌ |
|
||||
| Response StatusCodes | ✅ | ✅ | ✅ [^1] |
|
||||
| Response Headers | ✅ | ✅ | ✅ [^1] |
|
||||
| Response Body | ✅ | ✅ | ✅ |
|
||||
| Response Body Streaming | ❌ | ❌ | ✅ |
|
||||
| WebSockets | ❌ | ❌ | ❌ |
|
||||
| HTTP Redirects 30x | ✅ | ❌ | ❌ |
|
||||
|
||||
NOTE: Linux is currently very limited due to targeting a WebKit2GTK Version < 2.36.0. In the future some features will be
|
||||
supported by the introduction of WebKit2GTK 2.36.0+ support.
|
||||
[^1]: This requires WebKit2GTK 2.36+ support and your app needs to be build with the build tag `webkit2_36` to activate support for this feature. This also bumps the minimum requirement of WebKit2GTK to 2.36 for your app.
|
||||
|
||||
Name: AssetServer<br/>
|
||||
Type: `*assetserver.Options`
|
||||
|
@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add new property for the `wails.json` config file - `bindings`. More information on the new property can be found in the updated [schema](/schemas/config.v2.json). Properties `prefix` and `suffix` allow you to control the generated TypeScript entity name in the `model.ts` file. Added by @OlegGulevskyy in [PR](https://github.com/wailsapp/wails/pull/2101)
|
||||
- The `WindowSetAlwaysOnTop` method is now exposed in the JS runtime. Fixed by @gotid in [PR](https://github.com/wailsapp/wails/pull/2128)
|
||||
- The [AssetServer](/docs/reference/options#assetserver) now supports serving the index.html file when requesting a directory. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2110)
|
||||
- Added support for WebKit2GTK 2.36+ on Linux. This brings additional features for the [AssetServer](/docs/reference/options#assetserver), like support for HTTP methods and Headers. The app must be compiled with the Go build tag `webkit2_36` to activate support for this features. This also bumps the minimum requirement of WebKit2GTK to 2.36 for your app. Fixed by @stffabi in this [PR](https://github.com/wailsapp/wails/pull/2151)
|
||||
|
||||
### Fixed
|
||||
- The `noreload` flag in wails dev wasn't applied. Fixed by @stffabi in this [PR](https://github.com/wailsapp/wails/pull/2081)
|
||||
|
Loading…
Reference in New Issue
Block a user