Skip to content

Conversation

@Sammy-T
Copy link

@Sammy-T Sammy-T commented Sep 9, 2025

Description

This adds an option to run or build an app on Linux using webkitgtk-6.0.

Depends on:

  • gtk4

Fixes #3193

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Important

Features which have been removed/deprecated from webkit or gtk4 have been marked with todos.
If they can be reimplemented, using this library option could potentially match the functionality of the current implementation.

How Has This Been Tested?

  • Windows
  • macOS
  • Linux

Ubuntu 22.04

The performance is usually noticeable on an app with scrolled content or a complex layout.

Running/Building the app with this PR's changes requires:

  • Setting the app's Linux program name option
  • Adding -tags webkit_6 to the Wails dev or build command

Warnings:

Message Reason?
New application windows must be added after the GApplication::startup signal has been emitted. It should already be added after the startup signal so I think this warning is more about how it's set up.

Test Configuration

# Wails
Version         | v2.10.2
Package Manager | apt


# System
WARNING: failed to read int from file: open /sys/devices/system/cpu/cpu0/online: no such file or directory
┌──────────────────────────────────────────────────────────┐
| OS           | Ubuntu                                    |
| Version      | 22.04                                     |
| ID           | ubuntu                                    |
| Branding     |                                           |
| Go Version   | go1.24.5                                  |
| Platform     | linux                                     |
| Architecture | amd64                                     |
| CPU          | Intel(R) Core(TM) i5-6300HQ CPU @ 2.30GHz |
| Memory       | 16GB                                      |
└──────────────────────────────────────────────────────────┘

# Dependencies
┌──────────────────────────────────────────────────────────────────────────┐
| Dependency | Package Name          | Status    | Version                 |
| *docker    | docker.io             | Installed |                         |
| gcc        | build-essential       | Installed | 11.4.0                  |
| libgtk-3   | libgtk-3-dev          | Installed | 3.24.33-1ubuntu2.2      |
| libwebkit  | libwebkit2gtk-4.0-dev | Installed | 2.48.5-0ubuntu0.22.04.1 |
| npm        | npm                   | Installed | 11.5.2                  |
| *nsis      | nsis                  | Available | 3.08-2                  |
| pkg-config | pkg-config            | Installed | 0.29.2-1ubuntu3         |
|                                                                          |
└──────────────────────── * - Optional Dependency ─────────────────────────┘

# Diagnosis
Optional package(s) installation details:
  - nsis: sudo apt install nsis

Requires webkitgtk-6 and gtk4.

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Full Linux WebKit6/GTK4 desktop backend: window lifecycle, WebView integration, menus (submenus, checkboxes, radio groups, accelerators), DevTools/Inspector, drag & drop, file dialogs, multi-monitor support, and main-loop/init wiring.
  • Improvements

    • Enhanced clipboard read/write (primary + default) and refined Alt/Option modifier behavior.
    • Broader Linux compatibility: selectable WebKit/GTK variants at build time for wider environment support.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Walkthrough

Adds build-tagged support for WebKit 6 / GTK4 alongside existing WebKit2/GTK3 paths. Introduces new GTK4/WebKit6 implementations (window, menu, screen, clipboard, main-loop), many CGO/C helpers and headers, and adjusts pkg-config/cgo build constraints across frontend and assetserver webview files. (≤50 words)

Changes

Cohort / File(s) Summary
WebKit6 windowing
v2/internal/frontend/desktop/linux/window_webkit6.c, v2/internal/frontend/desktop/linux/window_webkit6.h, v2/internal/frontend/desktop/linux/window_webkit6.go, v2/internal/frontend/desktop/linux/loop_webkit6.go
New GTK4/WebKit6 window stack and API: app lifecycle, GMainLoop bridge, webview setup, IPC/invoke bridge, dialogs, inspector, drag/resize, window state/geometry, many C/CGO helpers and exported declarations.
WebKit6 menu integration
v2/internal/frontend/desktop/linux/menu_webkit6.go, v2/internal/frontend/desktop/linux/gtk_webkit6.go
New GTK4/WebKit6 menu construction, action map, accelerators, and C-callable handlers that dispatch menu, check, and radio actions to Go callbacks.
Screens & main loop (WebKit6)
v2/internal/frontend/desktop/linux/screen_webkit6.go, v2/internal/frontend/desktop/linux/loop_webkit6.go
New monitor enumeration (GetAllScreens) and exported setMainLoop GMainLoop exchange for GTK4/WebKit6 main-loop integration.
Frontend runtime & CGO helpers
v2/internal/frontend/desktop/linux/frontend.go, v2/internal/frontend/desktop/linux/frontend_c.go, v2/internal/frontend/desktop/linux/invoke.go
Add WEBKIT_6 conditionals, new initGtk()/runMainLoop() CGO delegates, edge constant mappings (GdkWindowEdge vs GdkSurfaceEdge), and cgo/pkg-config switching between gtk+-3.0 and gtk4.
Clipboard handling
v2/internal/frontend/desktop/linux/clipboard.go
Conditional clipboard get/set: WEBKIT_6 path uses GDK content provider and primary clipboard plus default clipboard; non-WEBKIT_6 retains GtkClipboard text API.
Input modifiers
v2/internal/frontend/desktop/linux/keys.go
Adds C.ALT_KEY_MASK (maps to GDK_ALT_MASK under WEBKIT_6, else GDK_MOD1_MASK) and updates modifier parsing to use it.
WebKit conditional build/config
v2/internal/frontend/desktop/linux/webkit2.go, v2/pkg/assetserver/webview/*webkit2_*.go, v2/pkg/assetserver/webview/request_linux.go, v2/pkg/assetserver/webview/responsewriter_linux.go, v2/pkg/assetserver/webview/webkit6.go
Introduces -DWEBKIT_6 path and pkg-config rules to support webkitgtk-6.0 + gtk4 versus existing webkit2gtk + gtk+-3.0; conditional header includes; adds exported Webkit2MinMinorVersion.
GTK3 / legacy files retagged (!webkit_6)
v2/internal/frontend/desktop/linux/gtk.go, v2/internal/frontend/desktop/linux/menu.go, v2/internal/frontend/desktop/linux/screen.go, v2/internal/frontend/desktop/linux/window.go, v2/internal/frontend/desktop/linux/window.c, v2/internal/frontend/desktop/linux/window.h
Files re-gated to linux && !webkit_6; cgo lines adjusted (unconditional gtk+-3.0 where applicable). No functional logic changes.
Assetserver webview adjustments
v2/pkg/assetserver/webview/request_linux.go, v2/pkg/assetserver/webview/responsewriter_linux.go, v2/pkg/assetserver/webview/webkit2_36+.go, v2/pkg/assetserver/webview/webkit2_40+.go, v2/pkg/assetserver/webview/webkit2_legacy.go, v2/pkg/assetserver/webview/webkit6.go
Expanded build-tag matrix to include webkit_6; conditional cgo/pkg-config adjustments and header includes to support WebKit6/GTK4 in assetserver webview components.
Small adjustments / reorders
v2/internal/frontend/desktop/linux/clipboard.go, v2/internal/frontend/desktop/linux/keys.go, v2/internal/frontend/desktop/linux/menu.go
Minor logic/import/order changes to support WEBKIT_6 variants (clipboard paths, ALT mask, import reordering).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App as Go App
    participant CGO as CGO bridge (initGtk / runMainLoop / setMainLoop)
    participant GTK as GTK runtime (gtk3 or gtk4)
    participant Window as Window (C/Go bridge)
    participant WebView as WebView (WebKit2 or WebKit6)
    participant Backend as Backend (Go)

    App->>CGO: initGtk()
    CGO-->>GTK: initialize GTK (gtk3 or gtk4) based on WEBKIT_6
    App->>Window: NewWindow / Run
    Window->>WebView: SetupWebview()
    WebView->>Backend: script-message -> processMessage (via CGO)
    Backend-->>WebView: respond / ExecuteJS
    Note right of Window: Menu, Clipboard, Screen use conditional WEBKIT_6 paths
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested labels

Enhancement, Linux, runtime, go, To Be Documented, size:XXL, lgtm

Suggested reviewers

  • leeanthony

Poem

I nibble code beneath the moonlit tree,
WebKit6 hops in, GTK4 sets me free.
Menus, clips, and screens learn new tricks with glee,
CGO bridges hum — main loops wake happily.
A rabbit cheers — the desktop springs to be 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.35% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Add webkitgtk6 option" is concise, specific, and directly describes the primary change in the changeset. The title accurately reflects the main objective of adding support for WebKitGTK 6.0 and GTK4 as a build option for Linux applications, which aligns with the substantial code changes introducing conditional compilation paths throughout the codebase via the WEBKIT_6 build tag.
Linked Issues Check ✅ Passed The pull request successfully addresses the core objectives of linked issue #3193: it introduces WebKitGTK 6.0 support through conditional compilation with the WEBKIT_6 build tag, integrates GTK4 as the required toolkit for WebKit6, maintains backward compatibility by preserving the existing webkit2gtk code paths, and implements this as an optional feature rather than a breaking migration. The implementation allows developers to opt-in via -tags webkit_6 while continuing to support the existing webkit2gtk approach, directly fulfilling the stated goals of avoiding future breakage from distribution removal of webkit2gtk-4.0 and enabling performance improvements.
Out of Scope Changes Check ✅ Passed All code changes in the pull request are directly aligned with adding WebKit6/GTK4 support as an optional build configuration. The modifications include: conditional compilation directives throughout Linux frontend files (clipboard, frontend initialization, key handling, menus, windows, screens), new WebKit6-specific implementation files, updated build tags to exclude WebKit6 paths from legacy code, and adjusted CGO directives to support both WebKit2 and WebKit6 variants. No extraneous changes unrelated to the WebKit6 migration objectives were identified in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b40dda and aa57d0b.

📒 Files selected for processing (2)
  • v2/internal/frontend/desktop/linux/window_webkit6.c (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
v2/internal/frontend/desktop/linux/window_webkit6.c (2)
v2/internal/frontend/runtime/desktop/draganddrop.js (1)
  • files (136-136)
v2/internal/frontend/desktop/linux/dialog.go (1)
  • GTK_FILE_CHOOSER_ACTION_SAVE (19-19)
v2/internal/frontend/desktop/linux/window_webkit6.h (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (39)
  • ExecuteOnMainThread (25-28)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • SetupInvokeSignal (130-133)
  • SetWindowIcon (136-152)
  • SetWindowTransparency (154-158)
  • SetBackgroundColour (162-209)
  • SetTitle (221-227)
  • SetPosition (242-254)
  • SetMinMaxSize (257-296)
  • DisableContextMenu (305-309)
  • ConnectButtons (356-366)
  • IsFullscreen (368-372)
  • IsMaximised (374-378)
  • IsMinimised (380-390)
  • Center (394-416)
  • Show (418-423)
  • Hide (425-430)
  • Maximise (432-437)
  • UnMaximise (439-444)
  • Minimise (446-451)
  • UnMinimise (453-458)
  • Fullscreen (460-476)
  • UnFullscreen (478-483)
  • SetupWebview (560-611)
  • LoadIndex (625-628)
  • DevtoolsEnabled (613-623)
  • ExecuteJS (684-690)
  • StartDrag (649-655)
  • StartResize (675-682)
  • MessageDialog (710-758)
  • AllocFileFilterArray (762-765)
  • Opendialog (815-902)
  • sendShowInspectorMessage (916-918)
  • ShowInspector (911-914)
  • InstallF12Hotkey (921-931)
  • createApp (939-943)
  • runApp (945-948)
🪛 Clang (14.0.6)
v2/internal/frontend/desktop/linux/window_webkit6.c

[error] 4-4: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

v2/internal/frontend/desktop/linux/window_webkit6.h

[error] 7-7: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (16)
v2/internal/frontend/desktop/linux/window_webkit6.c (13)

14-22: LGTM: Mouse tracking and Wayland detection state.

The static variables for drag tracking and Wayland detection are properly scoped and use appropriate GLib types.


24-127: LGTM: Helper functions are well-structured.

The helper functions properly handle NULL checks and error conditions. The past issue regarding NULL monitor handling in getCurrentMonitorScaleFactor has been addressed.


130-133: LGTM: Signal handler setup uses correct GLib types.

The function now correctly returns gulong matching the GLib API.


154-158: LGTM: Window transparency implementation.

The implementation now uses the correct guchar type and properly sets widget opacity.


298-366: LGTM: Context menu and button handling.

The GTK4 gesture-based event handling is correctly implemented with proper event type checks and button tracking.


368-390: LGTM: Window state queries correctly implemented.

The IsMinimised function now correctly checks the GDK_TOPLEVEL_STATE_MINIMIZED flag from the GTK4 toplevel state, as requested in past reviews.


418-483: LGTM: Window state transitions implemented.

The show/hide and maximize/minimize/fullscreen functions are correctly implemented using GTK4 APIs.


485-628: LGTM: Webview lifecycle and drag-and-drop implementation.

The webview setup, event handling, and drag-and-drop functionality are properly implemented. Memory management issues from past reviews have been addressed.


684-690: LGTM: JavaScript execution handler.

The function correctly evaluates JavaScript and properly frees the script memory.


692-692: LGTM: Correct extern declaration.

The function declaration uses the correct extern void syntax.


710-902: LGTM: Dialog implementations with appropriate deprecation notices.

The dialog implementations correctly handle GTK4 APIs. The TODO comments appropriately document deprecated APIs (lines 736-739, 824-827, 835-838, 866-869) that may need updating when minimum GTK version requirements change.


904-947: LGTM: Inspector and application lifecycle.

The inspector integration and GTK application lifecycle functions are correctly implemented. The activate callback is properly file-scoped.


162-209: Now I need to verify the actual implementation of invokeOnMainThread in the codebase to confirm whether it truly serializes execution:

Based on my comprehensive verification, here's the rewritten review comment:


Verify proper thread serialization for locale operations.

The C standard explicitly defines that calling setlocale() concurrently introduces data races. However, this code's save/restore pattern (lines 177-190) is valid if SetBackgroundColour executes exclusively on the GTK main thread.

Callbacks via g_idle_add execute in the main loop's thread with serial execution ("both callbacks will be run from the same thread so there is no chance of them being run at the same time"). Verify that invokeOnMainThread marshals all SetBackgroundColour calls to the GTK main thread to confirm the serialization guarantee.

v2/internal/frontend/desktop/linux/window_webkit6.h (3)

1-13: LGTM: Header structure and includes.

The header guards and includes are properly structured. The static analysis warning about jsc/jsc.h is a false positive—the header is provided by WebKitGTK6 at build time.


14-76: LGTM: Type definitions.

The struct typedefs are well-organized and use appropriate types for GTK4/WebKit6 integration.


78-133: LGTM: Function declarations.

All function declarations use correct types and signatures. Past issues regarding GLib type usage (gulong, guchar) and function signatures have been properly addressed.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@COD3HUNT3R COD3HUNT3R left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sammy-T
Copy link
Author

Sammy-T commented Sep 25, 2025

@COD3HUNT3R this is intended to work similarly to when using -tags webkit2_41 which doesn't have any additional output for the doctor command to my knowledge.

@COD3HUNT3R
Copy link

@COD3HUNT3R this is intended to work similarly to when using -tags webkit2_41 which doesn't have any additional output for the doctor command to my knowledge.

Yes, you are right but this would be helpful to find required packages for gtk4, webkit2-6.0

@Sammy-T
Copy link
Author

Sammy-T commented Sep 29, 2025

Had to revert updating from master (ca1eed6) because it broke local development on linux for me.

@Sammy-T Sammy-T marked this pull request as ready for review September 29, 2025 21:33
@Sammy-T
Copy link
Author

Sammy-T commented Sep 29, 2025

Running this on X11 Linux Mint, this appears to be working nicely.

I still don't know how/if I can get the todos (window positioning, etc.) working with webkit-6 though.

@leaanthony Are you able to test this and see how it runs for you? And do you know how to address the commented todos?

The redefinition might cause conflicts but it shouldn't be necessary.

(cherry picked from commit fcaddad)
(cherry picked from commit b0e94be)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (1)

45-81: LGTM! Message handling and Wayland detection are correct.

The WebKit message callback properly converts JSC values to strings and frees memory. Wayland detection correctly checks XDG_SESSION_TYPE and caches the result. Minor formatting: line 67 has inconsistent indentation (missing leading spaces).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0e94be and e5750dd.

📒 Files selected for processing (1)
  • v2/internal/frontend/desktop/linux/window_webkit6.c (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (3)
v2/internal/frontend/runtime/desktop/events.js (1)
  • data (105-105)
v2/internal/frontend/runtime/desktop/draganddrop.js (1)
  • files (136-136)
v2/internal/frontend/desktop/linux/dialog.go (1)
  • GTK_FILE_CHOOSER_ACTION_SAVE (19-19)
🔇 Additional comments (25)
v2/internal/frontend/desktop/linux/window_webkit6.c (25)

1-12: LGTM! Build tags and includes are correct.

The build tags properly restrict this implementation to Linux with the webkit_6 tag, and the includes cover GTK4, WebKit6, and JavaScriptCore APIs.


14-43: LGTM! Static state and helper functions are appropriate.

The static variables correctly track mouse drag state, and the cast helpers provide clean type conversions for GTK objects. ExecuteOnMainThread properly uses g_idle_add to marshal work to the GTK main thread.


83-121: LGTM! Monitor geometry helpers are well-implemented.

The functions correctly query GTK4's native surface and display APIs to retrieve monitor information, with appropriate null checks and error handling.


125-128: LGTM! Signal setup is correct.

The function properly connects the WebKit content manager's script message signal and returns the handler ID for later management.


130-147: SetWindowIcon remains a non-functional stub.

As noted in previous reviews, this function is entirely commented out and does not set the application icon. This is a known limitation of the current GTK4/webkit_6 implementation.


149-161: SetWindowTransparency remains a non-functional stub.

As flagged in previous reviews, this function is commented out. Translucent windows (appoptions.Linux.WindowIsTranslucent) will not work correctly without this implementation.


163-212: LGTM! Background color handling is well-implemented.

The function correctly sets both webview and window background colors. The locale manipulation ensures consistent float formatting, and the CSS provider singleton pattern is appropriate. The TODO on line 205 acknowledges the deprecation of gtk_css_provider_load_from_data since GTK 4.12, which is acceptable for backward compatibility.


214-230: LGTM! Title setting is correctly implemented.

The function properly marshals title updates to the main thread with correct memory management—both the title string and args struct are freed in the callback.


232-257: SetPosition remains a non-functional stub.

As noted in previous reviews, gtk_window_move was removed in GTK4, so programmatic window positioning is not supported in webkit_6.


259-299: SetMinMaxSize remains a non-functional stub.

As flagged in previous reviews, gtk_window_set_geometry_hints was removed in GTK4. This means appoptions.MinWidth, MinHeight, MaxWidth, and MaxHeight are not enforced in webkit_6 builds.


301-312: LGTM! Context menu disabling is correct.

The callback correctly returns TRUE to suppress the default WebKit context menu while allowing event propagation.


314-369: LGTM! Button event handling is correctly implemented.

The gesture controllers properly capture mouse button, position, and timestamp for window drag/resize operations. The implementation correctly uses GTK4's gesture API and only tracks left-button clicks.


389-413: Center remains a non-functional stub.

As noted in previous reviews, window centering is not functional because gtk_window_move was removed in GTK4. The Go code may still call this function, but it has no effect.


415-480: LGTM! Window state transitions are correctly implemented.

The functions properly use GTK4 APIs for show/hide, maximize/unmaximize, minimize/present, and fullscreen operations. Note that Fullscreen calls SetMinMaxSize (line 468), which is currently a no-op stub, but this doesn't impact fullscreen functionality since the window should fill the screen regardless.


482-504: LGTM! WebView load and window lifecycle callbacks are correct.

The load-changed handler properly signals "DomReady" on completion. The close button handler correctly sends "Q" and returns TRUE to prevent default close behavior.


506-585: Drag-and-drop remains incomplete.

As noted in previous reviews, the drag-and-drop implementation is non-functional. The old implementation is commented out (lines 508-570), and the current stub (lines 574-585) only prints file paths without processing them.


587-641: LGTM! WebView setup is well-implemented.

The function correctly initializes the WebKitWebView, registers the wails:// URI scheme, configures the user agent, and sets up hardware acceleration policies. The hide-on-close logic appropriately chooses between hiding or processing close events.


643-658: LGTM! Developer tools and URL loading are correctly implemented.

The functions properly enable WebKit developer extras and load URIs using the appropriate WebKit6 APIs.


660-685: Incorrect cast in gdk_toplevel_begin_move.

As flagged in previous reviews, line 672 passes options->mainwindow (a GtkWindow*) where a GdkToplevel* is expected. The correct approach is to obtain the GdkSurface via gtk_native_get_surface(GTK_NATIVE(options->mainwindow)) and cast it to GDK_TOPLEVEL(surface).


687-712: Incorrect cast in gdk_toplevel_begin_resize.

As flagged in previous reviews, line 699 has the same casting issue as line 672. The options->mainwindow must be converted to GdkToplevel* by obtaining the GdkSurface first.


722-788: LGTM! Message dialog implementation is correct.

The function properly handles different message types and buttons, with correct memory management. The TODO on line 766 appropriately acknowledges that gtk_message_dialog_new is deprecated since GTK 4.10, which is acceptable for backward compatibility.


790-843: LGTM! File dialog result handling is correct.

The function properly iterates through selected files using GTK4's GListModel API, with correct memory management. The 1024-file limit (line 823) is an arbitrary but reasonable constraint.


845-932: LGTM! File chooser dialog is well-implemented.

The function correctly creates and configures a file chooser dialog with proper memory management. Multiple TODOs (lines 854, 865, 896) appropriately acknowledge GTK 4.10 deprecations while maintaining backward compatibility.


934-978: LGTM! Inspector and application lifecycle functions are correctly implemented.

The functions properly handle WebKit inspector display, F12 hotkey registration, and GTK application creation/execution. The reference counting and signal connections are all correct.


714-720: Remove incorrect free(js) suggestion.

The JSCallback struct is stack-allocated in Go’s ExecJS and must not be freed in ExecuteJS; freeing only js->script is correct.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
v2/internal/frontend/desktop/linux/window_webkit6.go (2)

234-246: Document the GetPosition limitation for GTK4

The TODO comment notes that gtk_window_get_position was removed in GTK4, and the function returns (0, 0) as a placeholder. Consider documenting this limitation in the function's godoc so callers are aware of the behavior difference from the WebKit2 implementation.

Add a comment above the function:

+// GetPosition returns the window position. Note: GTK4 removed gtk_window_get_position,
+// so this always returns (0, 0). Callers should not rely on this for WebKit6 builds.
 func (w *Window) GetPosition() (int, int) {
 	//// TODO: gtk_window_get_position was removed in gtk4

67-68: Rename validation function to match WebKit6 usage
The call in window_webkit6.go still invokes validateWebKit2Version—either rename this function to something generic (e.g., validateWebKitVersion) or add a validateWebKit6Version variant and update its callers.

v2/internal/frontend/desktop/linux/window_webkit6.h (1)

88-93: Document stubbed window functions in header

Add a note in v2/internal/frontend/desktop/linux/window_webkit6.h that the following APIs are currently unimplemented no-ops under WebKit6/GTK4:

  • SetWindowIcon
  • SetWindowTransparency
  • SetPosition
  • SetMinMaxSize

Optionally, emit a runtime warning (e.g. via g_warning) in each C stub to notify users when these functions are invoked.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5750dd and 5e26557.

📒 Files selected for processing (4)
  • v2/internal/frontend/desktop/linux/gtk_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/menu_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
v2/internal/frontend/desktop/linux/gtk_webkit6.go (1)
v2/pkg/menu/callback.go (1)
  • CallbackData (3-6)
v2/internal/frontend/desktop/linux/window_webkit6.go (2)
v2/internal/frontend/desktop/linux/window.go (2)
  • NewWindow (62-149)
  • Window (40-53)
v2/internal/frontend/desktop/linux/window_webkit6.c (33)
  • SetupInvokeSignal (125-128)
  • SetupWebview (590-641)
  • ConnectButtons (359-369)
  • DevtoolsEnabled (643-653)
  • InstallF12Hotkey (951-961)
  • DisableContextMenu (308-312)
  • SetBackgroundColour (165-212)
  • SetTitle (224-230)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-161)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • Fullscreen (457-473)
  • ExecuteOnMainThread (25-28)
  • Center (391-413)
  • SetPosition (245-257)
  • SetMinMaxSize (260-299)
  • Show (415-420)
  • Hide (422-427)
  • Maximise (429-434)
  • Minimise (443-448)
  • IsFullscreen (371-375)
  • IsMaximised (377-381)
  • IsMinimised (383-387)
  • LoadIndex (655-658)
  • ExecuteJS (714-720)
  • StartDrag (679-685)
  • StartResize (705-712)
  • AllocFileFilterArray (792-795)
  • Opendialog (845-932)
  • MessageDialog (740-788)
  • ShowInspector (941-944)
v2/internal/frontend/desktop/linux/window_webkit6.h (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (39)
  • ExecuteOnMainThread (25-28)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • SetupInvokeSignal (125-128)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-161)
  • SetBackgroundColour (165-212)
  • SetTitle (224-230)
  • SetPosition (245-257)
  • SetMinMaxSize (260-299)
  • DisableContextMenu (308-312)
  • ConnectButtons (359-369)
  • IsFullscreen (371-375)
  • IsMaximised (377-381)
  • IsMinimised (383-387)
  • Center (391-413)
  • Show (415-420)
  • Hide (422-427)
  • Maximise (429-434)
  • UnMaximise (436-441)
  • Minimise (443-448)
  • UnMinimise (450-455)
  • Fullscreen (457-473)
  • UnFullscreen (475-480)
  • SetupWebview (590-641)
  • LoadIndex (655-658)
  • DevtoolsEnabled (643-653)
  • ExecuteJS (714-720)
  • StartDrag (679-685)
  • StartResize (705-712)
  • MessageDialog (740-788)
  • AllocFileFilterArray (792-795)
  • Opendialog (845-932)
  • sendShowInspectorMessage (946-948)
  • ShowInspector (941-944)
  • InstallF12Hotkey (951-961)
  • createApp (969-973)
  • runApp (975-978)
🔇 Additional comments (10)
v2/internal/frontend/desktop/linux/menu_webkit6.go (2)

73-77: LGTM: Proper concurrency-safe map usage

The conversion to sync.Map for gActionIdToMenuItem correctly addresses the concurrent access concerns raised in previous reviews, as this map is written here and read from CGO callbacks in gtk_webkit6.go.


87-97: Potential memory leak: Previous menu resources not freed

When SetApplicationMenu is called multiple times (e.g., during menu updates), the old GTK/GMenu objects cached in gtkMenuCache and the existing window.menubar are not freed before resetting the maps. This leaks native GTK resources.

Before resetting the maps, unref all cached GMenu objects and destroy the existing menubar:

 func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
 	if inmenu == nil {
 		return
 	}
 
+	// Clean up previous menu resources
+	for _, gmenu := range gtkMenuCache {
+		if gmenu != nil {
+			C.g_object_unref(C.gpointer(gmenu))
+		}
+	}
+	if w.menubar != nil {
+		C.gtk_widget_unparent(w.menubar)
+		w.menubar = nil
+	}
+
 	menuItemToId = make(map[*menu.MenuItem]int)
 	menuIdToItem = make(map[int]*menu.MenuItem)
 	gtkMenuCache = make(map[*menu.MenuItem]*C.GMenu)
 
 	processMenu(w, inmenu)
 }
v2/internal/frontend/desktop/linux/window_webkit6.go (2)

67-73: Good validation: GTK4 requires ProgramName

The fatal error when Linux.ProgramName is missing is appropriate because GTK4 applications require a valid application ID. This makes the requirement explicit to developers.


405-414: LGTM: Proper nil guard prevents double-unref

The nil check and subsequent assignment to nil correctly prevent double-unref issues if Quit() is called multiple times.

v2/internal/frontend/desktop/linux/window_webkit6.h (6)

127-127: Previous issue resolved: function signature now correct.

The declaration for sendShowInspectorMessage now correctly matches the implementation signature with parameters (GAction *action, GVariant *param).


7-12: LGTM: Includes are appropriate for WebKit6/GTK4.

The header includes cover JavaScript core, GTK4, WebKit, and standard C libraries as needed.


14-76: LGTM: Struct typedefs are well-defined.

All typedef structs are correctly defined with appropriate field types for GTK4/WebKit6 integration.


82-82: Correct decision to comment out GtkContainer.

GtkContainer was removed in GTK4, so commenting out this declaration is appropriate for the webkit_6 build path.


131-132: LGTM: GTK4 application lifecycle functions.

createApp and runApp correctly expose the GTK4 GtkApplication lifecycle management, which replaces the deprecated gtk_main approach from GTK3.


128-129: LGTM: Inspector function declarations match implementations.

The inspector functions (ShowInspector, InstallF12Hotkey) are correctly declared and match their implementations in window_webkit6.c.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
v2/internal/frontend/desktop/linux/window_webkit6.go (1)

133-135: Unused variable: buttonPressedName allocated but never used.

Line 133 allocates a C string "button-press-event" that is deferred-freed on line 134, but the variable is never passed to any function. This appears to be leftover code.

Apply this diff to remove the unused allocation:

 	result.webview = unsafe.Pointer(webview)
-	buttonPressedName := C.CString("button-press-event")
-	defer C.free(unsafe.Pointer(buttonPressedName))
 	C.ConnectButtons(unsafe.Pointer(webview))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e26557 and f60318e.

📒 Files selected for processing (1)
  • v2/internal/frontend/desktop/linux/window_webkit6.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
v2/internal/frontend/desktop/linux/window_webkit6.go (2)
v2/internal/frontend/desktop/linux/window.go (2)
  • NewWindow (62-149)
  • Window (40-53)
v2/internal/frontend/desktop/linux/window_webkit6.c (31)
  • SetupInvokeSignal (125-128)
  • SetupWebview (590-641)
  • ConnectButtons (359-369)
  • DevtoolsEnabled (643-653)
  • InstallF12Hotkey (951-961)
  • DisableContextMenu (308-312)
  • SetBackgroundColour (165-212)
  • SetTitle (224-230)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-161)
  • Fullscreen (457-473)
  • ExecuteOnMainThread (25-28)
  • UnFullscreen (475-480)
  • Center (391-413)
  • SetPosition (245-257)
  • SetMinMaxSize (260-299)
  • Show (415-420)
  • Hide (422-427)
  • Maximise (429-434)
  • Minimise (443-448)
  • IsFullscreen (371-375)
  • IsMaximised (377-381)
  • IsMinimised (383-387)
  • LoadIndex (655-658)
  • ExecuteJS (714-720)
  • StartDrag (679-685)
  • StartResize (705-712)
  • AllocFileFilterArray (792-795)
  • Opendialog (845-932)
  • MessageDialog (740-788)
  • ShowInspector (941-944)
🔇 Additional comments (7)
v2/internal/frontend/desktop/linux/window_webkit6.go (7)

383-387: Memory management fixed correctly.

The previous memory leak issue has been resolved. The C string is now allocated once, properly passed to SetTitle, and freed via defer.


405-414: Double-unref protection implemented correctly.

The previous issue has been addressed. The method now guards against double-unref by checking for nil and resetting mainLoop after cleanup.


459-465: Verify C function frees allocated strings.

Lines 460 and 464 allocate C strings for defaultFilename and defaultDirectory without immediate defer C.free. These are passed to the C function Opendialog.

Looking at the C implementation (lines 844-931 in window_webkit6.c), lines 853 and 868 show free(options->defaultDirectory) and free(options->defaultFilename), confirming the C side handles cleanup.

The memory ownership transfer is correct - C side frees these strings.


470-488: Memory management correct - C function frees strings.

The C strings for title and message are properly freed by the C side (lines 784-785 in window_webkit6.c).


389-395: ExecJS allocation is freed; no memory leak here. C.ExecuteJS unconditionally calls free(js->script) after evaluating the script, so the C.CString allocation in ExecJS is properly released.

Likely an incorrect or invalid review comment.


60-65: No action needed for activateWg synchronization. The package-level activateWg is called exactly once at startup in Linux (frontend.go), so there’s no concurrent reuse.


234-246: Linux WebKit6 GetPosition always returns (0, 0). All calls to WindowGetPosition on Linux with the webkit_6 build tag yield zero and there’s no downstream handling for this stub. Please document this limitation in the API or provide an alternative implementation.

Comment on lines +182 to +184
func (w *Window) asGTKBox() *C.GtkBox {
return C.GTKBOX(w.gtkWindow)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect type cast: asGTKBox casts gtkWindow instead of a box widget.

Line 183 casts w.gtkWindow to *C.GtkBox, but gtkWindow is a GtkWindow, not a box. This will cause type errors if the method is ever called. Likely should cast w.vbox or w.webviewBox.

Apply this diff to fix the cast:

 func (w *Window) asGTKBox() *C.GtkBox {
-	return C.GTKBOX(w.gtkWindow)
+	return C.GTKBOX(unsafe.Pointer(w.vbox))
 }
🤖 Prompt for AI Agents
In v2/internal/frontend/desktop/linux/window_webkit6.go around lines 182 to 184,
the asGTKBox method incorrectly casts w.gtkWindow to *C.GtkBox; change the cast
to the actual box field (e.g., w.vbox) so it returns C.GTKBOX(w.vbox) (or
w.webviewBox if that is the intended container), ensuring the returned GtkBox is
the correct widget rather than the GtkWindow.


C.gtk_window_present(w.asGTKWindow())

w.Center()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Center() call may be ineffective.

The C implementation of Center (lines 390-412 in window_webkit6.c from relevant snippets) is entirely commented out. This call does nothing and the window won't be centered.

Consider removing this call or implementing centering via GTK4 APIs:

 	gtk_window_present(w.asGTKWindow())
 
-	w.Center()
 	switch w.appoptions.WindowStartState {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
w.Center()
gtk_window_present(w.asGTKWindow())
switch w.appoptions.WindowStartState {
🤖 Prompt for AI Agents
In v2/internal/frontend/desktop/linux/window_webkit6.go around line 351, the
call to w.Center() is ineffective because the C implementation is commented out;
either remove this no-op call or replace it with a real GTK4-based centering
implementation: obtain the monitor workarea from the GDK display/monitor APIs,
compute centered x/y from the monitor workarea and the window size, and position
the window using the appropriate GTK4/GDK window movement API (or set window
size/constraints to achieve the same), ensuring any C bindings are updated to
call the GTK4 functions instead of the commented-out C Center implementation.

Comment on lines +502 to +516
// 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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential threading issue: C.MessageDialog not called on main thread.

Line 511 calls C.MessageDialog directly within a goroutine without using invokeOnMainThread. GTK functions must be called on the main thread. This could cause crashes or undefined behavior.

Apply this diff to fix:

-		C.MessageDialog(unsafe.Pointer(&data))
+		invokeOnMainThread(func() { C.MessageDialog(unsafe.Pointer(&data)) })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 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)
}
// 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),
}
invokeOnMainThread(func() { C.MessageDialog(unsafe.Pointer(&data)) })
}()
<-messageDialogResult
log.Fatal(message)
}
🤖 Prompt for AI Agents
In v2/internal/frontend/desktop/linux/window_webkit6.go around lines 502 to 516,
the code spawns a goroutine that calls C.MessageDialog which must run on the
main GTK thread; change the goroutine to call C.MessageDialog via the existing
invokeOnMainThread (or equivalent main-thread dispatcher) so the C.MessageDialog
invocation happens on the main thread, and ensure any C.CString allocations are
still created before dispatch and freed afterwards on the main thread as part of
that dispatched call.

Applying the original suggestion didn't account for it being freed from C but at least there's a comment to help indicate it now.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2da19c and c102d74.

📒 Files selected for processing (2)
  • v2/internal/frontend/desktop/linux/menu_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
v2/internal/frontend/desktop/linux/menu_webkit6.go (3)
v2/pkg/runtime/menu.go (2)
  • MenuSetApplicationMenu (9-12)
  • MenuUpdateApplicationMenu (14-17)
v2/internal/frontend/desktop/linux/window_webkit6.go (1)
  • Window (38-51)
v2/pkg/menu/menuitem.go (1)
  • SubMenu (319-329)
v2/internal/frontend/desktop/linux/window_webkit6.go (2)
v2/internal/frontend/desktop/linux/window.go (2)
  • NewWindow (62-149)
  • Window (40-53)
v2/internal/frontend/desktop/linux/window_webkit6.c (35)
  • SetupInvokeSignal (125-128)
  • SetupWebview (590-641)
  • ConnectButtons (359-369)
  • DevtoolsEnabled (643-653)
  • InstallF12Hotkey (951-961)
  • DisableContextMenu (308-312)
  • SetBackgroundColour (165-212)
  • SetTitle (224-230)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-161)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • Fullscreen (457-473)
  • ExecuteOnMainThread (25-28)
  • UnFullscreen (475-480)
  • Center (391-413)
  • SetMinMaxSize (260-299)
  • Show (415-420)
  • Hide (422-427)
  • Maximise (429-434)
  • UnMaximise (436-441)
  • Minimise (443-448)
  • UnMinimise (450-455)
  • IsFullscreen (371-375)
  • IsMaximised (377-381)
  • IsMinimised (383-387)
  • LoadIndex (655-658)
  • ExecuteJS (714-720)
  • StartDrag (679-685)
  • StartResize (705-712)
  • AllocFileFilterArray (792-795)
  • Opendialog (845-932)
  • MessageDialog (740-788)
  • ShowInspector (941-944)

Comment on lines +87 to +109
func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
if inmenu == nil {
return
}

// Clean up previous menu
for _, gmenu := range gtkMenuCache {
if gmenu != nil {
C.g_object_unref(C.gpointer(gmenu))
}
}

if w.menubar != nil {
C.gtk_widget_unparent(w.menubar)
w.menubar = nil
}

menuItemToId = make(map[*menu.MenuItem]int)
menuIdToItem = make(map[int]*menu.MenuItem)
gtkMenuCache = make(map[*menu.MenuItem]*C.GMenu)

processMenu(w, inmenu)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Persist the application menu before rebuilding

Line 87 returns immediately when inmenu is nil, and nowhere else do we assign to w.applicationMenu. As a result, MenuUpdateApplicationMenu() always sees a nil menu and becomes a no-op after the first call. Please store the menu pointer up front so refreshes keep working.

 func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
+	w.applicationMenu = inmenu
+
 	if inmenu == nil {
 		return
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
if inmenu == nil {
return
}
// Clean up previous menu
for _, gmenu := range gtkMenuCache {
if gmenu != nil {
C.g_object_unref(C.gpointer(gmenu))
}
}
if w.menubar != nil {
C.gtk_widget_unparent(w.menubar)
w.menubar = nil
}
menuItemToId = make(map[*menu.MenuItem]int)
menuIdToItem = make(map[int]*menu.MenuItem)
gtkMenuCache = make(map[*menu.MenuItem]*C.GMenu)
processMenu(w, inmenu)
}
func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
// Persist for future updates
w.applicationMenu = inmenu
if inmenu == nil {
return
}
// Clean up previous menu
for _, gmenu := range gtkMenuCache {
if gmenu != nil {
C.g_object_unref(C.gpointer(gmenu))
}
}
if w.menubar != nil {
C.gtk_widget_unparent(w.menubar)
w.menubar = nil
}
menuItemToId = make(map[*menu.MenuItem]int)
menuIdToItem = make(map[int]*menu.MenuItem)
gtkMenuCache = make(map[*menu.MenuItem]*C.GMenu)
processMenu(w, inmenu)
}
🤖 Prompt for AI Agents
v2/internal/frontend/desktop/linux/menu_webkit6.go around lines 87-109: the
function currently returns immediately when inmenu is nil and never assigns
w.applicationMenu, so future refreshes see nil; fix by persisting the incoming
menu pointer at the top (set w.applicationMenu = inmenu), and if inmenu is nil
clear any existing native menu state/caches and return; otherwise continue with
the existing cleanup and rebuild logic so MenuUpdateApplicationMenu() can find
the stored menu on subsequent calls.

@leaanthony
Copy link
Member

@Sammy-T - Thanks for your continued effort on this 🙏 Is it still a WIP? Wondering if we should convert this to draft?

@Sammy-T
Copy link
Author

Sammy-T commented Oct 2, 2025

@leaanthony Thanks. It's been a lot of work and I wasn't sure I'd even get this far. Sorry if I've been spamming you. I wasn't expecting to get hit with all those suggestions. Some of them were relevant. I can switch it back to draft.

Currently, this PR is working with the exception of missing some of the window control that was removed in gtk4 (I've marked them in the code as todo comments).

I was hoping you could test it out and also take a look at the todos to see if you know of any workarounds/updates?

@Sammy-T Sammy-T marked this pull request as draft October 2, 2025 02:28
This sets the transparency for the entire widget including the menu and the webview's contents.
@Sammy-T Sammy-T marked this pull request as ready for review October 17, 2025 21:56
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (5)
v2/internal/frontend/desktop/linux/window_webkit6.c (2)

387-411: Center/SetPosition/SetMinMaxSize are no-ops; document or feature-flag.

Calls from Go currently give the impression features exist.

  • Add explicit comments in Go calling sites noting these are no-ops on GTK4, or guard with build-tagged capabilities.

Also applies to: 235-249, 251-291


636-641: Incorrect argument type for gdk_toplevel_begin_move/resize.

Pass a GdkToplevel* from the window’s surface, not GtkWindow*.

-    gdk_toplevel_begin_move(options->mainwindow, NULL, mouseButton, xroot, yroot, dragTime);
+    GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(options->mainwindow));
+    gdk_toplevel_begin_move(GDK_TOPLEVEL(surface), NULL, mouseButton, xroot, yroot, dragTime);
@@
-    gdk_toplevel_begin_resize(options->mainwindow, options->edge, NULL, mouseButton, xroot, yroot, dragTime);
+    GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(options->mainwindow));
+    gdk_toplevel_begin_resize(GDK_TOPLEVEL(surface), options->edge, NULL, mouseButton, xroot, yroot, dragTime);

Also applies to: 663-667

v2/internal/frontend/desktop/linux/window_webkit6.go (3)

334-360: Wrap Run() GTK mutations on main thread and drop no-op Center().

GTK widget mutations must run on GTK thread. Also Center() is a no-op in GTK4 path.

- if w.menubar != nil {
-     C.gtk_box_prepend(C.GTKBOX(unsafe.Pointer(w.vbox)), w.menubar)
- }
-
- C.gtk_box_prepend(C.GTKBOX(unsafe.Pointer(w.webviewBox)), C.GTKWIDGET(w.webview))
- C.gtk_box_append(C.GTKBOX(unsafe.Pointer(w.vbox)), w.webviewBox)
-
- _url := C.CString(url)
- defer C.free(unsafe.Pointer(_url))
- C.LoadIndex(w.webview, _url)
+ _url := C.CString(url)
+ defer C.free(unsafe.Pointer(_url))
+ invokeOnMainThread(func() {
+     if w.menubar != nil {
+         C.gtk_box_prepend(C.GTKBOX(unsafe.Pointer(w.vbox)), w.menubar)
+     }
+     C.gtk_box_prepend(C.GTKBOX(unsafe.Pointer(w.webviewBox)), C.GTKWIDGET(w.webview))
+     C.gtk_box_append(C.GTKBOX(unsafe.Pointer(w.vbox)), w.webviewBox)
+     C.LoadIndex(w.webview, _url)
+     C.gtk_window_present(w.asGTKWindow())
+ })
  if w.appoptions.StartHidden {
      w.Hide()
  }
 
- C.gtk_window_present(w.asGTKWindow())
-
- w.Center()
  switch w.appoptions.WindowStartState {

501-511: GTK dialog called from goroutine; dispatch to main thread.

This is unsafe for GTK. Use invokeOnMainThread.

-    go func() {
-        data := C.MessageDialogOptions{
-            title:       C.CString(title),
-            message:     C.CString(message),
-            messageType: C.int(1),
-        }
-
-        C.MessageDialog(unsafe.Pointer(&data))
-    }()
+    go func() {
+        data := C.MessageDialogOptions{
+            title:       C.CString(title),
+            message:     C.CString(message),
+            messageType: C.int(1),
+        }
+        invokeOnMainThread(func() { C.MessageDialog(unsafe.Pointer(&data)) })
+    }()

182-184: Incorrect cast: asGTKBox must return the box widget, not the window.

Casting w.gtkWindow to GtkBox is invalid and may crash if used.

 func (w *Window) asGTKBox() *C.GtkBox {
-    return C.GTKBOX(w.gtkWindow)
+    return C.GTKBOX(unsafe.Pointer(w.vbox))
 }
🧹 Nitpick comments (6)
v2/internal/frontend/desktop/linux/window_webkit6.go (4)

133-135: Remove unused variable.

buttonPressedName is allocated and freed but never used.

- buttonPressedName := C.CString("button-press-event")
- defer C.free(unsafe.Pointer(buttonPressedName))
- C.ConnectButtons(unsafe.Pointer(webview))
+ C.ConnectButtons(unsafe.Pointer(webview))

362-365: SetKeepAbove is a stub; document or feature-flag it.

It does nothing on GTK4. Consider documenting the limitation or guarding the API to avoid misleading callers.


371-377: Prefer gtk_window_set_default_size OR provide a resize API; current SetSize duplicates SetDefaultSize.

If intentional, comment why. Otherwise, expose a Resize that changes current size.


222-232: Size() returns default size, not current size.

If the intent is current geometry, consider using a GTK4-appropriate getter (or document this).

v2/internal/frontend/desktop/linux/window_webkit6.c (2)

187-195: Do not unref the CSS provider if you reuse it later.

You store windowCssProvider globally and call load_from_data later; keep a strong ref.

-        windowCssProvider = gtk_css_provider_new();
-        gtk_style_context_add_provider(
+        windowCssProvider = gtk_css_provider_new();
+        gtk_style_context_add_provider(
             gtk_widget_get_style_context(GTK_WIDGET(options->webviewBox)),
             GTK_STYLE_PROVIDER(windowCssProvider),
             GTK_STYLE_PROVIDER_PRIORITY_USER);
-        g_object_unref(windowCssProvider);
+        // keep our reference; free when tearing down the window if needed

Also applies to: 197-204


551-552: Remove unused stub.

onDelete is defined but unused.

-static void onDelete(GtkWidget* self) {}
+// (removed unused stub)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c102d74 and e91a12c.

📒 Files selected for processing (3)
  • v2/internal/frontend/desktop/linux/window_webkit6.c (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
v2/internal/frontend/desktop/linux/window_webkit6.go (5)
v2/internal/frontend/desktop/linux/window.go (2)
  • NewWindow (62-149)
  • Window (40-53)
v2/internal/frontend/desktop/linux/window_webkit6.c (36)
  • SetupInvokeSignal (125-128)
  • SetupWebview (554-605)
  • ConnectButtons (351-361)
  • DevtoolsEnabled (607-617)
  • InstallF12Hotkey (915-925)
  • DisableContextMenu (300-304)
  • SetBackgroundColour (157-204)
  • SetTitle (216-222)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-153)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • Fullscreen (455-471)
  • ExecuteOnMainThread (25-28)
  • UnFullscreen (473-478)
  • Center (389-411)
  • SetPosition (237-249)
  • SetMinMaxSize (252-291)
  • Show (413-418)
  • Hide (420-425)
  • Maximise (427-432)
  • UnMaximise (434-439)
  • Minimise (441-446)
  • UnMinimise (448-453)
  • IsFullscreen (363-367)
  • IsMaximised (369-373)
  • IsMinimised (375-385)
  • LoadIndex (619-622)
  • ExecuteJS (678-684)
  • StartDrag (643-649)
  • StartResize (669-676)
  • AllocFileFilterArray (756-759)
  • Opendialog (809-896)
  • MessageDialog (704-752)
  • ShowInspector (905-908)
v2/pkg/options/linux/linux.go (2)
  • WebviewGpuPolicy (4-4)
  • WebviewGpuPolicyNever (12-12)
v2/pkg/options/options.go (5)
  • DragAndDrop (201-216)
  • RGBA (111-116)
  • WindowStartState (22-22)
  • Minimised (27-27)
  • Maximised (26-26)
v2/internal/frontend/desktop/linux/calloc.go (1)
  • NewCalloc (18-20)
v2/internal/frontend/desktop/linux/window_webkit6.c (3)
v2/internal/frontend/runtime/desktop/events.js (1)
  • data (105-105)
v2/internal/frontend/runtime/desktop/draganddrop.js (1)
  • files (136-136)
v2/internal/frontend/desktop/linux/dialog.go (1)
  • GTK_FILE_CHOOSER_ACTION_SAVE (19-19)
v2/internal/frontend/desktop/linux/window_webkit6.h (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (39)
  • ExecuteOnMainThread (25-28)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • SetupInvokeSignal (125-128)
  • SetWindowIcon (131-147)
  • SetWindowTransparency (149-153)
  • SetBackgroundColour (157-204)
  • SetTitle (216-222)
  • SetPosition (237-249)
  • SetMinMaxSize (252-291)
  • DisableContextMenu (300-304)
  • ConnectButtons (351-361)
  • IsFullscreen (363-367)
  • IsMaximised (369-373)
  • IsMinimised (375-385)
  • Center (389-411)
  • Show (413-418)
  • Hide (420-425)
  • Maximise (427-432)
  • UnMaximise (434-439)
  • Minimise (441-446)
  • UnMinimise (448-453)
  • Fullscreen (455-471)
  • UnFullscreen (473-478)
  • SetupWebview (554-605)
  • LoadIndex (619-622)
  • DevtoolsEnabled (607-617)
  • ExecuteJS (678-684)
  • StartDrag (643-649)
  • StartResize (669-676)
  • MessageDialog (704-752)
  • AllocFileFilterArray (756-759)
  • Opendialog (809-896)
  • sendShowInspectorMessage (910-912)
  • ShowInspector (905-908)
  • InstallF12Hotkey (915-925)
  • createApp (933-937)
  • runApp (939-942)
🪛 Clang (14.0.6)
v2/internal/frontend/desktop/linux/window_webkit6.c

[error] 4-4: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

v2/internal/frontend/desktop/linux/window_webkit6.h

[error] 7-7: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (1)
v2/internal/frontend/desktop/linux/window_webkit6.go (1)

6-12: Based on my research, the original JavaScriptCore API (e.g. JSContextRef and JSObjectRef) has been replaced by the GObject-style JavaScriptCore API (e.g. [[email protected]] and [[email protected]]) that is available since 2.22, and this is available in javascriptcoregtk-6.0. Additionally, the Ubuntu bug tracker shows that jsc/jsc.h: No such file or directory errors occur when the header is not properly included, and multiple real-world examples demonstrate that projects building with WebKit report missing javascriptcoregtk as a separate dependency requirement.

The review comment is technically sound: the code includes <jsc/jsc.h>, which requires a separate pkg-config declaration for javascriptcoregtk-6.0. The suggested diff is correct.

Add missing pkg-config for JavaScriptCore to fix 'jsc/jsc.h' include.

The JavaScriptCore 6.0 API is a separate package that must be declared in pkg-config flags. Without javascriptcoregtk-6.0, compilation will fail.

Apply:

 /*
-#cgo pkg-config: gtk4 webkitgtk-6.0
+#cgo pkg-config: gtk4 webkitgtk-6.0 javascriptcoregtk-6.0

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (9)
v2/internal/frontend/desktop/linux/window_webkit6.go (5)

350-351: Center() is a no‑op on GTK4; drop the call or annotate.

Avoid misleading no‑op calls until implemented.

Apply this diff:

-	w.Center()
+	// Center() is currently a no-op on GTK4/webkit_6.

327-331: SetWindowIcon currently calls a stub.

No icon will be set under webkit_6. Either implement in C or gate this call behind availability and log a single warning.


181-183: Fix incorrect cast in asGTKBox().

Casting GtkWindow to GtkBox is invalid; return the actual box.

Apply this diff:

 func (w *Window) asGTKBox() *C.GtkBox {
-	return C.GTKBOX(w.gtkWindow)
+	return C.GTKBOX(unsafe.Pointer(w.vbox))
 }

403-412: Quit() is a no‑op on GTK4; call GApplication quit.

When mainLoop is nil, the app never exits. Keep the guard and also quit the GApplication on the GTK thread.

Apply this diff:

 func (w *Window) Quit() {
-	if mainLoop == nil {
-		return
-	}
-
-	C.g_main_loop_quit(mainLoop)
-	C.g_main_loop_unref(mainLoop)
-
-	mainLoop = nil
+	if mainLoop != nil {
+		C.g_main_loop_quit(mainLoop)
+		C.g_main_loop_unref(mainLoop)
+		mainLoop = nil
+	}
+	invokeOnMainThread(func() {
+		C.g_application_quit((*C.GApplication)(unsafe.Pointer(w.gtkApp)))
+	})
 }

500-514: Call MessageDialog on the GTK main thread (no goroutine).

GTK APIs must run on the main thread.

Apply this diff:

 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))
-	}()
+	invokeOnMainThread(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)
 }
v2/internal/frontend/desktop/linux/window_webkit6.c (4)

393-416: Center() is intentionally a no‑op (GTK4 removed move).

Either implement a GTK4 alternative later or keep as documented no‑op. Ensure the Go call site doesn’t invoke it (see window_webkit6.go Lines 350‑351).


642-647: Fix type passed to gdk_toplevel_begin_move.

First arg must be GdkToplevel*. Convert from GtkWindow via GtkNative/GdkSurface.

Apply this diff:

-    gdk_toplevel_begin_move(options->mainwindow, NULL, mouseButton, xroot, yroot, dragTime);
+    GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(options->mainwindow));
+    gdk_toplevel_begin_move(GDK_TOPLEVEL(surface), NULL, mouseButton, xroot, yroot, dragTime);

669-673: Fix type passed to gdk_toplevel_begin_resize.

Same conversion needed as for move.

Apply this diff:

-    gdk_toplevel_begin_resize(options->mainwindow, options->edge, NULL, mouseButton, xroot, yroot, dragTime);
+    GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(options->mainwindow));
+    gdk_toplevel_begin_resize(GDK_TOPLEVEL(surface), options->edge, NULL, mouseButton, xroot, yroot, dragTime);

760-760: Fix invalid C prototype order (“void extern”).

C requires “extern void …”.

Apply this diff:

-void extern processOpenFileResult(void *);
+extern void processOpenFileResult(void *);
🧹 Nitpick comments (2)
v2/internal/frontend/desktop/linux/window_webkit6.go (1)

6-9: Surface required system packages in “doctor” for -tags webkit_6.

Add doctor checks to verify gtk4 and webkitgtk-6.0 dev packages and headers (e.g., jsc/jsc.h). This will preempt build failures.

v2/internal/frontend/desktop/linux/window_webkit6.h (1)

7-9: Build deps: ensure jsc/jsc.h is available.

Document that webkitgtk-6.0 (packages providing JavaScriptCore headers) must be installed; consider adding a doctor check.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e91a12c and 1b40dda.

📒 Files selected for processing (3)
  • v2/internal/frontend/desktop/linux/window_webkit6.c (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.go (1 hunks)
  • v2/internal/frontend/desktop/linux/window_webkit6.h (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
v2/internal/frontend/desktop/linux/window_webkit6.go (4)
v2/internal/frontend/desktop/linux/window.go (2)
  • NewWindow (62-149)
  • Window (40-53)
v2/internal/frontend/desktop/linux/window_webkit6.c (36)
  • SetupInvokeSignal (130-133)
  • SetupWebview (560-611)
  • ConnectButtons (356-366)
  • DevtoolsEnabled (613-623)
  • InstallF12Hotkey (921-931)
  • DisableContextMenu (305-309)
  • SetBackgroundColour (162-209)
  • SetTitle (221-227)
  • SetWindowIcon (136-152)
  • SetWindowTransparency (154-158)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • Fullscreen (460-476)
  • ExecuteOnMainThread (25-28)
  • UnFullscreen (478-483)
  • Center (394-416)
  • SetPosition (242-254)
  • SetMinMaxSize (257-296)
  • Show (418-423)
  • Hide (425-430)
  • Maximise (432-437)
  • UnMaximise (439-444)
  • Minimise (446-451)
  • UnMinimise (453-458)
  • IsFullscreen (368-372)
  • IsMaximised (374-378)
  • IsMinimised (380-390)
  • LoadIndex (625-628)
  • ExecuteJS (684-690)
  • StartDrag (649-655)
  • StartResize (675-682)
  • AllocFileFilterArray (762-765)
  • Opendialog (815-902)
  • MessageDialog (710-758)
  • ShowInspector (911-914)
v2/pkg/options/linux/linux.go (2)
  • WebviewGpuPolicy (4-4)
  • WebviewGpuPolicyNever (12-12)
v2/internal/frontend/desktop/linux/calloc.go (1)
  • NewCalloc (18-20)
v2/internal/frontend/desktop/linux/window_webkit6.c (2)
v2/internal/frontend/runtime/desktop/draganddrop.js (1)
  • files (136-136)
v2/internal/frontend/desktop/linux/dialog.go (1)
  • GTK_FILE_CHOOSER_ACTION_SAVE (19-19)
v2/internal/frontend/desktop/linux/window_webkit6.h (1)
v2/internal/frontend/desktop/linux/window_webkit6.c (39)
  • ExecuteOnMainThread (25-28)
  • GTKWIDGET (30-33)
  • GTKWINDOW (35-38)
  • GTKBOX (40-43)
  • SetupInvokeSignal (130-133)
  • SetWindowIcon (136-152)
  • SetWindowTransparency (154-158)
  • SetBackgroundColour (162-209)
  • SetTitle (221-227)
  • SetPosition (242-254)
  • SetMinMaxSize (257-296)
  • DisableContextMenu (305-309)
  • ConnectButtons (356-366)
  • IsFullscreen (368-372)
  • IsMaximised (374-378)
  • IsMinimised (380-390)
  • Center (394-416)
  • Show (418-423)
  • Hide (425-430)
  • Maximise (432-437)
  • UnMaximise (439-444)
  • Minimise (446-451)
  • UnMinimise (453-458)
  • Fullscreen (460-476)
  • UnFullscreen (478-483)
  • SetupWebview (560-611)
  • LoadIndex (625-628)
  • DevtoolsEnabled (613-623)
  • ExecuteJS (684-690)
  • StartDrag (649-655)
  • StartResize (675-682)
  • MessageDialog (710-758)
  • AllocFileFilterArray (762-765)
  • Opendialog (815-902)
  • sendShowInspectorMessage (916-918)
  • ShowInspector (911-914)
  • InstallF12Hotkey (921-931)
  • createApp (939-943)
  • runApp (945-948)
🪛 Clang (14.0.6)
v2/internal/frontend/desktop/linux/window_webkit6.c

[error] 4-4: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

v2/internal/frontend/desktop/linux/window_webkit6.h

[error] 7-7: 'jsc/jsc.h' file not found

(clang-diagnostic-error)

Comment on lines +233 to +245
func (w *Window) GetPosition() (int, int) {
//// TODO: gtk_window_get_position was removed in gtk4
// var width, height C.int
// var wg sync.WaitGroup
// wg.Add(1)
// invokeOnMainThread(func() {
// C.gtk_window_get_position(w.asGTKWindow(), &width, &height)
// wg.Done()
// })
// wg.Wait()
// return int(width), int(height)
return 0, 0
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Position/min/max size are stubs; options are silently ignored.

GetPosition returns (0,0) and SetMin/MaxSize call a no-op C function. Either implement a GTK4-friendly fallback (size-allocate clamping) or clearly document the limitation and log once at startup.

Would you like a follow-up patch that clamps sizes via size-allocate and signals?

Also applies to: 247-261

🤖 Prompt for AI Agents
In v2/internal/frontend/desktop/linux/window_webkit6.go around lines 233-245
(and similarly for 247-261), GetPosition currently returns a hardcoded (0,0) and
SetMin/SetMaxSize are no-ops which silently ignore limits; replace these stubs
with a GTK4-compatible implementation or add a clear one-time startup log and
documented limitation: implement size clamping by connecting to the
"size-allocate" signal on the GTK window (on the main thread) to enforce min/max
constraints and maintain/return the last known position from allocate/configure
events, or if you cannot implement clamping now, add a single startup log
warning explaining that position/min/max are not supported on GTK4 and ensure
the functions return deterministic values and do not silently ignore inputs.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
6 Security Hotspots
24.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@Sammy-T
Copy link
Author

Sammy-T commented Oct 17, 2025

@leaanthony I don't see any workarounds for gtk4's removal of window positioning and I'll respect that that's the direction they want to go in so I've just left it as is. I've fixed checking window minimization state. Drag and drop should be working now. And I added window transparency but just be aware that the transparency affects the entire widget including the menu and all of the webview's contents.

I'm marking this as ready. I've done all I can and it does successfully utilize webkitgtk-6.

@Sammy-T
Copy link
Author

Sammy-T commented Nov 1, 2025

@leaanthony Are you able to look this over?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade to webkitgtk-6.0

3 participants