chore: merge debug/gtk-segv into dev-test
6 files · +94 -38
M internal/app/browser/browser.go +12 -7
| @@ -422,15 +422,20 @@ func (app *BrowserApp) setupSignalHandling() { | |||
| 422 | 422 | // runMainLoop starts the appropriate main loop based on WebKit availability | |
| 423 | 423 | func (app *BrowserApp) runMainLoop() { | |
| 424 | 424 | if webkit.IsNativeAvailable() { | |
| 425 | - | // Schedule content blocking initialization to run on the first main loop iteration | |
| 426 | - | // This ensures GTK is fully ready before we start loading filters | |
| 427 | - | webkit.RunOnMainThread(func() { | |
| 428 | - | if err := app.setupContentBlocking(); err != nil { | |
| 429 | - | logging.Warn(fmt.Sprintf("Warning: failed to setup content blocking: %v", err)) | |
| 430 | - | } | |
| 431 | - | }) | |
| 425 | + | if os.Getenv("DUMBER_DISABLE_CONTENT_BLOCKING") != "1" { | |
| 426 | + | // Schedule content blocking initialization to run on the first main loop iteration | |
| 427 | + | // This ensures GTK is fully ready before we start loading filters | |
| 428 | + | webkit.RunOnMainThread(func() { | |
| 429 | + | if err := app.setupContentBlocking(); err != nil { | |
| 430 | + | logging.Warn(fmt.Sprintf("Warning: failed to setup content blocking: %v", err)) | |
| 431 | + | } | |
| 432 | + | }) | |
| 433 | + | } else { | |
| 434 | + | logging.Warn("Content blocking skipped (DUMBER_DISABLE_CONTENT_BLOCKING=1)") | |
| 435 | + | } | |
| 432 | 436 | ||
| 433 | 437 | logging.Info(fmt.Sprintf("Entering GTK main loop…")) | |
| 438 | + | logging.Debug("[browser] Calling webkit.RunMainLoop()") | |
| 434 | 439 | webkit.RunMainLoop() | |
| 435 | 440 | logging.Info(fmt.Sprintf("GTK main loop exited")) | |
| 436 | 441 | ||
| @@ -422,15 +422,20 @@ func (app *BrowserApp) setupSignalHandling() { | |||||
| 422 | // runMainLoop starts the appropriate main loop based on WebKit availability | 422 | // runMainLoop starts the appropriate main loop based on WebKit availability | ||
| 423 | func (app *BrowserApp) runMainLoop() { | 423 | func (app *BrowserApp) runMainLoop() { | ||
| 424 | if webkit.IsNativeAvailable() { | 424 | if webkit.IsNativeAvailable() { | ||
| 425 | - | // Schedule content blocking initialization to run on the first main loop iteration | 425 | + | if os.Getenv("DUMBER_DISABLE_CONTENT_BLOCKING") != "1" { |
| 426 | - | // This ensures GTK is fully ready before we start loading filters | 426 | + | // Schedule content blocking initialization to run on the first main loop iteration |
| 427 | - | webkit.RunOnMainThread(func() { | 427 | + | // This ensures GTK is fully ready before we start loading filters |
| 428 | - | if err := app.setupContentBlocking(); err != nil { | 428 | + | webkit.RunOnMainThread(func() { |
| 429 | - | logging.Warn(fmt.Sprintf("Warning: failed to setup content blocking: %v", err)) | 429 | + | if err := app.setupContentBlocking(); err != nil { |
| 430 | - | } | 430 | + | logging.Warn(fmt.Sprintf("Warning: failed to setup content blocking: %v", err)) |
| 431 | - | }) | 431 | + | } |
| 432 | + | }) | |||
| 433 | + | } else { | |||
| 434 | + | logging.Warn("Content blocking skipped (DUMBER_DISABLE_CONTENT_BLOCKING=1)") | |||
| 435 | + | } | |||
| 432 | 436 | ||||
| 433 | logging.Info(fmt.Sprintf("Entering GTK main loop…")) | 437 | logging.Info(fmt.Sprintf("Entering GTK main loop…")) | ||
| 438 | + | logging.Debug("[browser] Calling webkit.RunMainLoop()") | |||
| 434 | webkit.RunMainLoop() | 439 | webkit.RunMainLoop() | ||
| 435 | logging.Info(fmt.Sprintf("GTK main loop exited")) | 440 | logging.Info(fmt.Sprintf("GTK main loop exited")) | ||
| 436 | 441 | ||||
M internal/app/browser/webview.go +7 -0
| @@ -373,6 +373,13 @@ func (app *BrowserApp) initializeFaviconService() error { | |||
| 373 | 373 | func (app *BrowserApp) setupContentBlocking() error { | |
| 374 | 374 | logging.Info(fmt.Sprintf("Initializing content blocking system...")) | |
| 375 | 375 | ||
| 376 | + | // Temporary kill-switch for crash triage: set env DUMBER_DISABLE_CONTENT_BLOCKING=1 | |
| 377 | + | // to skip all blocking setup and run bare WebViews. | |
| 378 | + | if os.Getenv("DUMBER_DISABLE_CONTENT_BLOCKING") == "1" { | |
| 379 | + | logging.Warn("Content blocking disabled via DUMBER_DISABLE_CONTENT_BLOCKING=1 (test mode)") | |
| 380 | + | return nil | |
| 381 | + | } | |
| 382 | + | ||
| 376 | 383 | // Enable WebKit debug logging if requested | |
| 377 | 384 | webkit.SetupWebKitDebugLogging() | |
| 378 | 385 | ||
| @@ -373,6 +373,13 @@ func (app *BrowserApp) initializeFaviconService() error { | |||||
| 373 | func (app *BrowserApp) setupContentBlocking() error { | 373 | func (app *BrowserApp) setupContentBlocking() error { | ||
| 374 | logging.Info(fmt.Sprintf("Initializing content blocking system...")) | 374 | logging.Info(fmt.Sprintf("Initializing content blocking system...")) | ||
| 375 | 375 | ||||
| 376 | + | // Temporary kill-switch for crash triage: set env DUMBER_DISABLE_CONTENT_BLOCKING=1 | |||
| 377 | + | // to skip all blocking setup and run bare WebViews. | |||
| 378 | + | if os.Getenv("DUMBER_DISABLE_CONTENT_BLOCKING") == "1" { | |||
| 379 | + | logging.Warn("Content blocking disabled via DUMBER_DISABLE_CONTENT_BLOCKING=1 (test mode)") | |||
| 380 | + | return nil | |||
| 381 | + | } | |||
| 382 | + | ||||
| 376 | // Enable WebKit debug logging if requested | 383 | // Enable WebKit debug logging if requested | ||
| 377 | webkit.SetupWebKitDebugLogging() | 384 | webkit.SetupWebKitDebugLogging() | ||
| 378 | 385 | ||||
M internal/config/defaults.go +1 -1
| @@ -136,7 +136,7 @@ func DefaultConfig() *Config { | |||
| 136 | 136 | AV1HardwareOnly: false, // Allow software AV1 fallback | |
| 137 | 137 | DisableVP9Hardware: false, // Allow VP9 hardware for high res content | |
| 138 | 138 | VideoBufferSizeMB: 64, // Larger buffer for bursty Twitch/AV1 segments | |
| 139 | - | QueueBufferTimeSec: 20, // More buffering time for smooth playback | |
| 139 | + | QueueBufferTimeSec: 3, // Standard buffering time (was 20s, reduced for faster load completion) | |
| 140 | 140 | CustomUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", // WebKitGTK default (same as Epiphany) | |
| 141 | 141 | AV1MaxResolution: "1080p", // Optimal AV1 up to 1080p, fallback to VP9 for higher res | |
| 142 | 142 | DisableTwitchCodecControl: true, // Disable codec control on Twitch by default (prevents theater/fullscreen freezing) | |
| @@ -136,7 +136,7 @@ func DefaultConfig() *Config { | |||||
| 136 | AV1HardwareOnly: false, // Allow software AV1 fallback | 136 | AV1HardwareOnly: false, // Allow software AV1 fallback | ||
| 137 | DisableVP9Hardware: false, // Allow VP9 hardware for high res content | 137 | DisableVP9Hardware: false, // Allow VP9 hardware for high res content | ||
| 138 | VideoBufferSizeMB: 64, // Larger buffer for bursty Twitch/AV1 segments | 138 | VideoBufferSizeMB: 64, // Larger buffer for bursty Twitch/AV1 segments | ||
| 139 | - | QueueBufferTimeSec: 20, // More buffering time for smooth playback | 139 | + | QueueBufferTimeSec: 3, // Standard buffering time (was 20s, reduced for faster load completion) |
| 140 | CustomUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", // WebKitGTK default (same as Epiphany) | 140 | CustomUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", // WebKitGTK default (same as Epiphany) | ||
| 141 | AV1MaxResolution: "1080p", // Optimal AV1 up to 1080p, fallback to VP9 for higher res | 141 | AV1MaxResolution: "1080p", // Optimal AV1 up to 1080p, fallback to VP9 for higher res | ||
| 142 | DisableTwitchCodecControl: true, // Disable codec control on Twitch by default (prevents theater/fullscreen freezing) | 142 | DisableTwitchCodecControl: true, // Disable codec control on Twitch by default (prevents theater/fullscreen freezing) | ||
M pkg/webkit/errors.go +32 -12
| @@ -1,20 +1,40 @@ | |||
| 1 | 1 | package webkit | |
| 2 | 2 | ||
| 3 | - | import "errors" | |
| 3 | + | import ( | |
| 4 | + | "errors" | |
| 5 | + | ||
| 6 | + | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | |
| 7 | + | "github.com/diamondburned/gotk4/pkg/core/gerror" | |
| 8 | + | ) | |
| 4 | 9 | ||
| 5 | 10 | var ( | |
| 6 | - | // ErrNotImplemented indicates a feature is not yet implemented | |
| 7 | - | ErrNotImplemented = errors.New("webkit: not implemented") | |
| 11 | + | ErrWebViewNotInitialized = errors.New("webkit: WebView not initialized") | |
| 12 | + | ErrWebViewDestroyed = errors.New("webkit: WebView destroyed") | |
| 13 | + | ErrInvalidURL = errors.New("webkit: invalid URL") | |
| 14 | + | ) | |
| 8 | 15 | ||
| 9 | - | // ErrWebViewNotInitialized indicates the WebView was not properly initialized | |
| 10 | - | ErrWebViewNotInitialized = errors.New("webkit: webview not initialized") | |
| 16 | + | // IsExclusivelyCancelledError returns true if the error is ONLY a cancellation error. | |
| 17 | + | // It checks both the error string and the GError code. | |
| 18 | + | func IsExclusivelyCancelledError(err error) bool { | |
| 19 | + | if err == nil { | |
| 20 | + | return false | |
| 21 | + | } | |
| 11 | 22 | ||
| 12 | - | // ErrWebViewDestroyed indicates the WebView has been destroyed | |
| 13 | - | ErrWebViewDestroyed = errors.New("webkit: webview destroyed") | |
| 23 | + | // 1. Check string message (fastest, covers many cases) | |
| 24 | + | if err.Error() == "Load request cancelled" { | |
| 25 | + | return true | |
| 26 | + | } | |
| 14 | 27 | ||
| 15 | - | // ErrInvalidURL indicates an invalid URL was provided | |
| 16 | - | ErrInvalidURL = errors.New("webkit: invalid URL") | |
| 28 | + | // 2. Check GError code if available | |
| 29 | + | var gErr *gerror.GError | |
| 30 | + | if errors.As(err, &gErr) { | |
| 31 | + | // Calculate the expected quark/domain if possible, or just check the code. | |
| 32 | + | // WebKitNetworkErrorCancelled is 302 (WEBKIT_NETWORK_ERROR_CANCELLED) | |
| 33 | + | // We trust the code uniqueness within the WebKit domain context usually. | |
| 34 | + | if gErr.ErrorCode() == int(webkit.NetworkErrorCancelled) { | |
| 35 | + | return true | |
| 36 | + | } | |
| 37 | + | } | |
| 17 | 38 | ||
| 18 | - | // ErrMainThreadRequired indicates an operation must be called from the main thread | |
| 19 | - | ErrMainThreadRequired = errors.New("webkit: operation must be called from main thread") | |
| 20 | - | ) | |
| 39 | + | return false | |
| 40 | + | } | |
| @@ -1,20 +1,40 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | - | import "errors" | 3 | + | import ( |
| 4 | + | "errors" | |||
| 5 | + | ||||
| 6 | + | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | |||
| 7 | + | "github.com/diamondburned/gotk4/pkg/core/gerror" | |||
| 8 | + | ) | |||
| 4 | 9 | ||||
| 5 | var ( | 10 | var ( | ||
| 6 | - | // ErrNotImplemented indicates a feature is not yet implemented | 11 | + | ErrWebViewNotInitialized = errors.New("webkit: WebView not initialized") |
| 7 | - | ErrNotImplemented = errors.New("webkit: not implemented") | 12 | + | ErrWebViewDestroyed = errors.New("webkit: WebView destroyed") |
| 13 | + | ErrInvalidURL = errors.New("webkit: invalid URL") | |||
| 14 | + | ) | |||
| 8 | 15 | ||||
| 9 | - | // ErrWebViewNotInitialized indicates the WebView was not properly initialized | 16 | + | // IsExclusivelyCancelledError returns true if the error is ONLY a cancellation error. |
| 10 | - | ErrWebViewNotInitialized = errors.New("webkit: webview not initialized") | 17 | + | // It checks both the error string and the GError code. |
| 18 | + | func IsExclusivelyCancelledError(err error) bool { | |||
| 19 | + | if err == nil { | |||
| 20 | + | return false | |||
| 21 | + | } | |||
| 11 | 22 | ||||
| 12 | - | // ErrWebViewDestroyed indicates the WebView has been destroyed | 23 | + | // 1. Check string message (fastest, covers many cases) |
| 13 | - | ErrWebViewDestroyed = errors.New("webkit: webview destroyed") | 24 | + | if err.Error() == "Load request cancelled" { |
| 25 | + | return true | |||
| 26 | + | } | |||
| 14 | 27 | ||||
| 15 | - | // ErrInvalidURL indicates an invalid URL was provided | 28 | + | // 2. Check GError code if available |
| 16 | - | ErrInvalidURL = errors.New("webkit: invalid URL") | 29 | + | var gErr *gerror.GError |
| 30 | + | if errors.As(err, &gErr) { | |||
| 31 | + | // Calculate the expected quark/domain if possible, or just check the code. | |||
| 32 | + | // WebKitNetworkErrorCancelled is 302 (WEBKIT_NETWORK_ERROR_CANCELLED) | |||
| 33 | + | // We trust the code uniqueness within the WebKit domain context usually. | |||
| 34 | + | if gErr.ErrorCode() == int(webkit.NetworkErrorCancelled) { | |||
| 35 | + | return true | |||
| 36 | + | } | |||
| 37 | + | } | |||
| 17 | 38 | ||||
| 18 | - | // ErrMainThreadRequired indicates an operation must be called from the main thread | 39 | + | return false |
| 19 | - | ErrMainThreadRequired = errors.New("webkit: operation must be called from main thread") | 40 | + | } |
| 20 | - | ) | |||
M pkg/webkit/mainloop.go +23 -8
| @@ -3,6 +3,7 @@ package webkit | |||
| 3 | 3 | import ( | |
| 4 | 4 | "runtime" | |
| 5 | 5 | ||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |
| 6 | 7 | glib "github.com/diamondburned/gotk4/pkg/glib/v2" | |
| 7 | 8 | gtk "github.com/diamondburned/gotk4/pkg/gtk/v4" | |
| 8 | 9 | ) | |
| @@ -10,6 +11,7 @@ import ( | |||
| 10 | 11 | var ( | |
| 11 | 12 | mainLoop *glib.MainLoop | |
| 12 | 13 | isInitialized bool | |
| 14 | + | mainContext *glib.MainContext | |
| 13 | 15 | ) | |
| 14 | 16 | ||
| 15 | 17 | // InitMainThread locks the current goroutine to the OS thread for GTK operations | |
| @@ -21,6 +23,9 @@ func InitMainThread() { | |||
| 21 | 23 | // Initialize GTK - this is required before creating any GTK widgets | |
| 22 | 24 | gtk.Init() | |
| 23 | 25 | ||
| 26 | + | // Cache the default main context so we can check ownership later. | |
| 27 | + | mainContext = glib.MainContextDefault() | |
| 28 | + | ||
| 24 | 29 | isInitialized = true | |
| 25 | 30 | } | |
| 26 | 31 | } | |
| @@ -34,7 +39,9 @@ func RunMainLoop() { | |||
| 34 | 39 | mainLoop = glib.NewMainLoop(nil, false) | |
| 35 | 40 | } | |
| 36 | 41 | ||
| 42 | + | logging.Debug("[webkit] Starting GLib main loop (mainLoop.Run)") | |
| 37 | 43 | mainLoop.Run() | |
| 44 | + | logging.Debug("[webkit] GLib main loop exited") | |
| 38 | 45 | } | |
| 39 | 46 | ||
| 40 | 47 | // QuitMainLoop stops the GTK main event loop. | |
| @@ -44,18 +51,26 @@ func QuitMainLoop() { | |||
| 44 | 51 | } | |
| 45 | 52 | } | |
| 46 | 53 | ||
| 47 | - | // IsMainThread returns true if GTK has been initialized. | |
| 48 | - | // Note: This doesn't truly detect the main thread, but is used as a guard | |
| 49 | - | // for whether GTK operations are safe. All GTK operations should use | |
| 50 | - | // RunOnMainThread or IdleAdd to be thread-safe. | |
| 54 | + | // IsMainThread returns true if called from the GTK main thread. | |
| 51 | 55 | func IsMainThread() bool { | |
| 52 | - | return isInitialized | |
| 56 | + | if !isInitialized { | |
| 57 | + | return false | |
| 58 | + | } | |
| 59 | + | if mainContext == nil { | |
| 60 | + | mainContext = glib.MainContextDefault() | |
| 61 | + | } | |
| 62 | + | return mainContext != nil && mainContext.IsOwner() | |
| 53 | 63 | } | |
| 54 | 64 | ||
| 55 | - | // RunOnMainThread schedules a function to run on the GTK main thread. | |
| 56 | - | // Always uses glib.IdleAdd to ensure thread safety. | |
| 57 | - | // The function will execute during the next main loop iteration. | |
| 65 | + | // RunOnMainThread executes a function on the GTK main thread. | |
| 66 | + | // If already on the main thread, executes immediately. | |
| 67 | + | // Otherwise, schedules the function via glib.IdleAdd. | |
| 58 | 68 | func RunOnMainThread(fn func()) { | |
| 69 | + | if IsMainThread() { | |
| 70 | + | fn() | |
| 71 | + | return | |
| 72 | + | } | |
| 73 | + | ||
| 59 | 74 | glib.IdleAdd(func() bool { | |
| 60 | 75 | fn() | |
| 61 | 76 | return false // Remove the idle handler after execution | |
| @@ -3,6 +3,7 @@ package webkit | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "runtime" | 4 | "runtime" | ||
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | glib "github.com/diamondburned/gotk4/pkg/glib/v2" | 7 | glib "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| 7 | gtk "github.com/diamondburned/gotk4/pkg/gtk/v4" | 8 | gtk "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 8 | ) | 9 | ) | ||
| @@ -10,6 +11,7 @@ import ( | |||||
| 10 | var ( | 11 | var ( | ||
| 11 | mainLoop *glib.MainLoop | 12 | mainLoop *glib.MainLoop | ||
| 12 | isInitialized bool | 13 | isInitialized bool | ||
| 14 | + | mainContext *glib.MainContext | |||
| 13 | ) | 15 | ) | ||
| 14 | 16 | ||||
| 15 | // InitMainThread locks the current goroutine to the OS thread for GTK operations | 17 | // InitMainThread locks the current goroutine to the OS thread for GTK operations | ||
| @@ -21,6 +23,9 @@ func InitMainThread() { | |||||
| 21 | // Initialize GTK - this is required before creating any GTK widgets | 23 | // Initialize GTK - this is required before creating any GTK widgets | ||
| 22 | gtk.Init() | 24 | gtk.Init() | ||
| 23 | 25 | ||||
| 26 | + | // Cache the default main context so we can check ownership later. | |||
| 27 | + | mainContext = glib.MainContextDefault() | |||
| 28 | + | ||||
| 24 | isInitialized = true | 29 | isInitialized = true | ||
| 25 | } | 30 | } | ||
| 26 | } | 31 | } | ||
| @@ -34,7 +39,9 @@ func RunMainLoop() { | |||||
| 34 | mainLoop = glib.NewMainLoop(nil, false) | 39 | mainLoop = glib.NewMainLoop(nil, false) | ||
| 35 | } | 40 | } | ||
| 36 | 41 | ||||
| 42 | + | logging.Debug("[webkit] Starting GLib main loop (mainLoop.Run)") | |||
| 37 | mainLoop.Run() | 43 | mainLoop.Run() | ||
| 44 | + | logging.Debug("[webkit] GLib main loop exited") | |||
| 38 | } | 45 | } | ||
| 39 | 46 | ||||
| 40 | // QuitMainLoop stops the GTK main event loop. | 47 | // QuitMainLoop stops the GTK main event loop. | ||
| @@ -44,18 +51,26 @@ func QuitMainLoop() { | |||||
| 44 | } | 51 | } | ||
| 45 | } | 52 | } | ||
| 46 | 53 | ||||
| 47 | - | // IsMainThread returns true if GTK has been initialized. | 54 | + | // IsMainThread returns true if called from the GTK main thread. |
| 48 | - | // Note: This doesn't truly detect the main thread, but is used as a guard | |||
| 49 | - | // for whether GTK operations are safe. All GTK operations should use | |||
| 50 | - | // RunOnMainThread or IdleAdd to be thread-safe. | |||
| 51 | func IsMainThread() bool { | 55 | func IsMainThread() bool { | ||
| 52 | - | return isInitialized | 56 | + | if !isInitialized { |
| 57 | + | return false | |||
| 58 | + | } | |||
| 59 | + | if mainContext == nil { | |||
| 60 | + | mainContext = glib.MainContextDefault() | |||
| 61 | + | } | |||
| 62 | + | return mainContext != nil && mainContext.IsOwner() | |||
| 53 | } | 63 | } | ||
| 54 | 64 | ||||
| 55 | - | // RunOnMainThread schedules a function to run on the GTK main thread. | 65 | + | // RunOnMainThread executes a function on the GTK main thread. |
| 56 | - | // Always uses glib.IdleAdd to ensure thread safety. | 66 | + | // If already on the main thread, executes immediately. |
| 57 | - | // The function will execute during the next main loop iteration. | 67 | + | // Otherwise, schedules the function via glib.IdleAdd. |
| 58 | func RunOnMainThread(fn func()) { | 68 | func RunOnMainThread(fn func()) { | ||
| 69 | + | if IsMainThread() { | |||
| 70 | + | fn() | |||
| 71 | + | return | |||
| 72 | + | } | |||
| 73 | + | ||||
| 59 | glib.IdleAdd(func() bool { | 74 | glib.IdleAdd(func() bool { | ||
| 60 | fn() | 75 | fn() | ||
| 61 | return false // Remove the idle handler after execution | 76 | return false // Remove the idle handler after execution | ||
M pkg/webkit/webview.go +19 -10
| @@ -64,6 +64,7 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||
| 64 | 64 | id := atomic.AddUint64(&viewIDCounter, 1) | |
| 65 | 65 | logging.Debug(fmt.Sprintf("[webkit] Generated WebView ID: %d (CreateWindow=%v)", id, cfg.CreateWindow)) | |
| 66 | 66 | ||
| 67 | + | logging.Debug("[webkit] Initializing main thread") | |
| 67 | 68 | InitMainThread() | |
| 68 | 69 | ||
| 69 | 70 | // Initialize persistent session - REQUIRED, no ephemeral fallback | |
| @@ -254,6 +255,7 @@ func (w *WebView) setupEventHandlers() { | |||
| 254 | 255 | w.view.Connect("notify::title", func() { | |
| 255 | 256 | if w.onTitleChanged != nil { | |
| 256 | 257 | title := w.view.Title() | |
| 258 | + | logging.Debug(fmt.Sprintf("[webkit] notify::title signal: %q", title)) | |
| 257 | 259 | w.onTitleChanged(title) | |
| 258 | 260 | } | |
| 259 | 261 | }) | |
| @@ -262,6 +264,7 @@ func (w *WebView) setupEventHandlers() { | |||
| 262 | 264 | w.view.Connect("notify::uri", func() { | |
| 263 | 265 | if w.onURIChanged != nil { | |
| 264 | 266 | uri := w.view.URI() | |
| 267 | + | logging.Debug(fmt.Sprintf("[webkit] notify::uri signal: %q", uri)) | |
| 265 | 268 | w.onURIChanged(uri) | |
| 266 | 269 | } | |
| 267 | 270 | }) | |
| @@ -276,14 +279,18 @@ func (w *WebView) setupEventHandlers() { | |||
| 276 | 279 | ||
| 277 | 280 | // Load committed - connect to load-changed signal for WEBKIT_LOAD_COMMITTED | |
| 278 | 281 | w.view.ConnectLoadChanged(func(loadEvent webkit.LoadEvent) { | |
| 282 | + | logging.Debug(fmt.Sprintf("[webkit] LoadChanged signal: %v", loadEvent)) | |
| 279 | 283 | if loadEvent == webkit.LoadStarted && w.onLoadStarted != nil { | |
| 284 | + | logging.Debug("[webkit] calling onLoadStarted") | |
| 280 | 285 | w.onLoadStarted() | |
| 281 | 286 | } | |
| 282 | 287 | if loadEvent == webkit.LoadCommitted && w.onLoadCommitted != nil { | |
| 283 | 288 | uri := w.view.URI() | |
| 289 | + | logging.Debug(fmt.Sprintf("[webkit] calling onLoadCommitted: %q", uri)) | |
| 284 | 290 | w.onLoadCommitted(uri) | |
| 285 | 291 | } | |
| 286 | 292 | if loadEvent == webkit.LoadFinished && w.onLoadFinished != nil { | |
| 293 | + | logging.Debug("[webkit] calling onLoadFinished") | |
| 287 | 294 | w.onLoadFinished() | |
| 288 | 295 | } | |
| 289 | 296 | }) | |
| @@ -296,16 +303,17 @@ func (w *WebView) setupEventHandlers() { | |||
| 296 | 303 | }) | |
| 297 | 304 | ||
| 298 | 305 | // Load failed - connect to load-failed signal for network errors, content blocking, etc. | |
| 299 | - | w.view.ConnectLoadFailed(func(loadEvent webkit.LoadEvent, failingUri string, err error) bool { | |
| 300 | - | if w.onLoadFailed != nil { | |
| 301 | - | errorMsg := "Unknown error" | |
| 302 | - | if err != nil { | |
| 303 | - | errorMsg = err.Error() | |
| 304 | - | } | |
| 305 | - | w.onLoadFailed(failingUri, errorMsg) | |
| 306 | - | } | |
| 307 | - | return false // Let WebKit show its default error page | |
| 308 | - | }) | |
| 306 | + | // CRASH FIX: Disabling this handler because it causes a SIGABRT (free(): invalid pointer) | |
| 307 | + | // likely due to issues with gotk4 signal marshalling/memory management for the error argument. | |
| 308 | + | /* | |
| 309 | + | w.view.ConnectLoadFailed(func(loadEvent webkit.LoadEvent, failingUri string, err error) bool { | |
| 310 | + | logging.Debug(fmt.Sprintf("[webkit] LoadFailed signal: event=%v uri=%s", loadEvent, failingUri)) | |
| 311 | + | ||
| 312 | + | // DEBUG TEST: Blindly return true regardless of error type to see if crash persists. | |
| 313 | + | logging.Debug("[webkit] LoadFailed: Suppressing error page (Blind Test)") | |
| 314 | + | return true | |
| 315 | + | }) | |
| 316 | + | */ | |
| 309 | 317 | ||
| 310 | 318 | // Favicon changed - connect to FaviconDatabase | |
| 311 | 319 | w.setupFaviconHandlers() | |
| @@ -358,6 +366,7 @@ func (w *WebView) setupEventHandlers() { | |||
| 358 | 366 | if w.onScriptMessage != nil && jscValue != nil { | |
| 359 | 367 | // Convert JSCValue to string using gotk4 javascriptcore bindings | |
| 360 | 368 | valueStr := JSCValueToString(jscValue) | |
| 369 | + | logging.Debug(fmt.Sprintf("[webkit] script-message-received: %s", valueStr)) | |
| 361 | 370 | if valueStr != "" { | |
| 362 | 371 | w.onScriptMessage(valueStr) | |
| 363 | 372 | } | |
| @@ -64,6 +64,7 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||||
| 64 | id := atomic.AddUint64(&viewIDCounter, 1) | 64 | id := atomic.AddUint64(&viewIDCounter, 1) | ||
| 65 | logging.Debug(fmt.Sprintf("[webkit] Generated WebView ID: %d (CreateWindow=%v)", id, cfg.CreateWindow)) | 65 | logging.Debug(fmt.Sprintf("[webkit] Generated WebView ID: %d (CreateWindow=%v)", id, cfg.CreateWindow)) | ||
| 66 | 66 | ||||
| 67 | + | logging.Debug("[webkit] Initializing main thread") | |||
| 67 | InitMainThread() | 68 | InitMainThread() | ||
| 68 | 69 | ||||
| 69 | // Initialize persistent session - REQUIRED, no ephemeral fallback | 70 | // Initialize persistent session - REQUIRED, no ephemeral fallback | ||
| @@ -254,6 +255,7 @@ func (w *WebView) setupEventHandlers() { | |||||
| 254 | w.view.Connect("notify::title", func() { | 255 | w.view.Connect("notify::title", func() { | ||
| 255 | if w.onTitleChanged != nil { | 256 | if w.onTitleChanged != nil { | ||
| 256 | title := w.view.Title() | 257 | title := w.view.Title() | ||
| 258 | + | logging.Debug(fmt.Sprintf("[webkit] notify::title signal: %q", title)) | |||
| 257 | w.onTitleChanged(title) | 259 | w.onTitleChanged(title) | ||
| 258 | } | 260 | } | ||
| 259 | }) | 261 | }) | ||
| @@ -262,6 +264,7 @@ func (w *WebView) setupEventHandlers() { | |||||
| 262 | w.view.Connect("notify::uri", func() { | 264 | w.view.Connect("notify::uri", func() { | ||
| 263 | if w.onURIChanged != nil { | 265 | if w.onURIChanged != nil { | ||
| 264 | uri := w.view.URI() | 266 | uri := w.view.URI() | ||
| 267 | + | logging.Debug(fmt.Sprintf("[webkit] notify::uri signal: %q", uri)) | |||
| 265 | w.onURIChanged(uri) | 268 | w.onURIChanged(uri) | ||
| 266 | } | 269 | } | ||
| 267 | }) | 270 | }) | ||
| @@ -276,14 +279,18 @@ func (w *WebView) setupEventHandlers() { | |||||
| 276 | 279 | ||||
| 277 | // Load committed - connect to load-changed signal for WEBKIT_LOAD_COMMITTED | 280 | // Load committed - connect to load-changed signal for WEBKIT_LOAD_COMMITTED | ||
| 278 | w.view.ConnectLoadChanged(func(loadEvent webkit.LoadEvent) { | 281 | w.view.ConnectLoadChanged(func(loadEvent webkit.LoadEvent) { | ||
| 282 | + | logging.Debug(fmt.Sprintf("[webkit] LoadChanged signal: %v", loadEvent)) | |||
| 279 | if loadEvent == webkit.LoadStarted && w.onLoadStarted != nil { | 283 | if loadEvent == webkit.LoadStarted && w.onLoadStarted != nil { | ||
| 284 | + | logging.Debug("[webkit] calling onLoadStarted") | |||
| 280 | w.onLoadStarted() | 285 | w.onLoadStarted() | ||
| 281 | } | 286 | } | ||
| 282 | if loadEvent == webkit.LoadCommitted && w.onLoadCommitted != nil { | 287 | if loadEvent == webkit.LoadCommitted && w.onLoadCommitted != nil { | ||
| 283 | uri := w.view.URI() | 288 | uri := w.view.URI() | ||
| 289 | + | logging.Debug(fmt.Sprintf("[webkit] calling onLoadCommitted: %q", uri)) | |||
| 284 | w.onLoadCommitted(uri) | 290 | w.onLoadCommitted(uri) | ||
| 285 | } | 291 | } | ||
| 286 | if loadEvent == webkit.LoadFinished && w.onLoadFinished != nil { | 292 | if loadEvent == webkit.LoadFinished && w.onLoadFinished != nil { | ||
| 293 | + | logging.Debug("[webkit] calling onLoadFinished") | |||
| 287 | w.onLoadFinished() | 294 | w.onLoadFinished() | ||
| 288 | } | 295 | } | ||
| 289 | }) | 296 | }) | ||
| @@ -296,16 +303,17 @@ func (w *WebView) setupEventHandlers() { | |||||
| 296 | }) | 303 | }) | ||
| 297 | 304 | ||||
| 298 | // Load failed - connect to load-failed signal for network errors, content blocking, etc. | 305 | // Load failed - connect to load-failed signal for network errors, content blocking, etc. | ||
| 299 | - | w.view.ConnectLoadFailed(func(loadEvent webkit.LoadEvent, failingUri string, err error) bool { | 306 | + | // CRASH FIX: Disabling this handler because it causes a SIGABRT (free(): invalid pointer) |
| 300 | - | if w.onLoadFailed != nil { | 307 | + | // likely due to issues with gotk4 signal marshalling/memory management for the error argument. |
| 301 | - | errorMsg := "Unknown error" | 308 | + | /* |
| 302 | - | if err != nil { | 309 | + | w.view.ConnectLoadFailed(func(loadEvent webkit.LoadEvent, failingUri string, err error) bool { |
| 303 | - | errorMsg = err.Error() | 310 | + | logging.Debug(fmt.Sprintf("[webkit] LoadFailed signal: event=%v uri=%s", loadEvent, failingUri)) |
| 304 | - | } | 311 | + | |
| 305 | - | w.onLoadFailed(failingUri, errorMsg) | 312 | + | // DEBUG TEST: Blindly return true regardless of error type to see if crash persists. |
| 306 | - | } | 313 | + | logging.Debug("[webkit] LoadFailed: Suppressing error page (Blind Test)") |
| 307 | - | return false // Let WebKit show its default error page | 314 | + | return true |
| 308 | - | }) | 315 | + | }) |
| 316 | + | */ | |||
| 309 | 317 | ||||
| 310 | // Favicon changed - connect to FaviconDatabase | 318 | // Favicon changed - connect to FaviconDatabase | ||
| 311 | w.setupFaviconHandlers() | 319 | w.setupFaviconHandlers() | ||
| @@ -358,6 +366,7 @@ func (w *WebView) setupEventHandlers() { | |||||
| 358 | if w.onScriptMessage != nil && jscValue != nil { | 366 | if w.onScriptMessage != nil && jscValue != nil { | ||
| 359 | // Convert JSCValue to string using gotk4 javascriptcore bindings | 367 | // Convert JSCValue to string using gotk4 javascriptcore bindings | ||
| 360 | valueStr := JSCValueToString(jscValue) | 368 | valueStr := JSCValueToString(jscValue) | ||
| 369 | + | logging.Debug(fmt.Sprintf("[webkit] script-message-received: %s", valueStr)) | |||
| 361 | if valueStr != "" { | 370 | if valueStr != "" { | ||
| 362 | w.onScriptMessage(valueStr) | 371 | w.onScriptMessage(valueStr) | ||
| 363 | } | 372 | } | ||