v0.0.70 - Fix: WebKit inspector window X button not closing — handle open-window signal with custom GtkWindow

This commit is contained in:
Laan Tungir
2026-08-11 11:39:09 -04:00
parent 2bd544e54b
commit 6174d9369e
4 changed files with 205 additions and 42 deletions
+1 -1
View File
@@ -1 +1 @@
0.0.69
0.0.70
+111
View File
@@ -0,0 +1,111 @@
#!/bin/bash
# install_local.sh — Build sovereign_browser from local source and install to
# /usr/local/bin (or $INSTALL_PREFIX/bin).
#
# This is a lightweight alternative to install.sh for developers who already
# have the build environment set up. It does NOT install apt dependencies,
# FIPS, or Tor — it just runs `make` and copies the binary.
#
# Usage:
# ./install_local.sh # build + install to /usr/local
# INSTALL_PREFIX=/opt ./install_local.sh # install elsewhere
# ./install_local.sh --prefix /opt # same
# ./install_local.sh --help # show help
set -euo pipefail
INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}"
# --- Output helpers ------------------------------------------------------
if [[ -t 2 && -z "${NO_COLOR:-}" ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
else
RED='' GREEN='' YELLOW='' BLUE='' NC=''
fi
print_info() { echo -e "${BLUE}[INFO]${NC} $*" >&2; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $*" >&2; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $*" >&2; }
print_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
die() { print_error "$*"; exit 1; }
# --- Privilege helper ----------------------------------------------------
# Returns "sudo" if the target path is not user-writable, else "".
sudo_for() {
local target="$1"
if [[ -w "$target" ]]; then
echo ""
else
command -v sudo >/dev/null 2>&1 || die "sudo is required to write to $target but is not available."
echo "sudo"
fi
}
# --- Usage ---------------------------------------------------------------
show_usage() {
cat <<'EOF'
install_local.sh — Build sovereign_browser from local source and install
Usage: ./install_local.sh [options]
Options:
--prefix <dir> Install prefix (default: /usr/local)
-h, --help Show this help
Environment:
INSTALL_PREFIX Alternative to --prefix (default: /usr/local)
EOF
}
# --- Args ----------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--prefix) INSTALL_PREFIX="${2:-}"; shift 2 ;;
-h|--help) show_usage; exit 0 ;;
*) die "Unknown option: $1 (try --help)" ;;
esac
done
# --- Build ---------------------------------------------------------------
print_info "Building sovereign_browser from local source..."
if ! make; then
die "Build failed. Ensure build dependencies are installed (see Makefile)."
fi
[[ -x ./sovereign_browser ]] || die "Build finished but ./sovereign_browser was not produced."
print_success "Build complete."
# --- Install binary ------------------------------------------------------
prefix_bin="$INSTALL_PREFIX/bin"
SUDO="$(sudo_for "$INSTALL_PREFIX")"
print_info "Installing binary to $prefix_bin/sovereign_browser"
$SUDO mkdir -p "$prefix_bin"
$SUDO cp -f ./sovereign_browser "$prefix_bin/sovereign_browser"
$SUDO chmod +x "$prefix_bin/sovereign_browser"
print_success "Binary installed: $prefix_bin/sovereign_browser"
# --- Done ----------------------------------------------------------------
print_success "sovereign_browser installed to $prefix_bin"
if [[ ":${PATH}:" != *":$prefix_bin:"* ]]; then
print_warning "$prefix_bin is not in your PATH. Add it: export PATH=\"$prefix_bin:\$PATH\""
fi
cat >&2 <<EOF
${GREEN}[SUCCESS]${NC} Next steps:
Run: sovereign_browser
Or: sovereign_browser --login-method generate
Or: sovereign_browser --login-method generate --url https://example.com
Data dir: ~/.sovereign_browser/
EOF
+91 -39
View File
@@ -2034,11 +2034,19 @@ static void on_menu_stop(GtkMenuItem *item, gpointer data) {
}
}
/* ── Inspector window position/size persistence ────────────────────── *
* When the inspector detaches into its own window, we track the window's
* position and size via configure-event and save to settings. On the
* next show, we restore the saved geometry. */
/* ── Inspector window management ───────────────────────────────────── *
* We handle the WebKitWebInspector::open-window signal ourselves to
* create a proper GtkWindow for the inspector. WebKit's default window
* doesn't properly handle title bar button clicks (X, minimize, maximize)
* in some environments, so we create our own window with full control
* over event handling. We also persist the window position/size. */
/* Track whether the inspector is currently shown for the active tab.
* We use a per-tab flag stored in a static hash (inspector state is
* per-webview, but we track it globally for the toggle). */
static gboolean g_inspector_visible = FALSE;
/* Persist the inspector window's position and size. */
static gboolean on_inspector_window_configure(GtkWidget *widget,
GdkEventConfigure *event,
gpointer user_data) {
@@ -2053,45 +2061,82 @@ static gboolean on_inspector_window_configure(GtkWidget *widget,
return FALSE;
}
/* Find the detached inspector's toplevel GtkWindow and hook configure-event.
* Called when the inspector's WebView is realized (after detach). */
static void on_inspector_webview_realize(GtkWidget *widget,
/* Handle the X button (delete-event) on our custom inspector window.
* Close the inspector via the API (so ::closed fires and the visibility
* flag resets), then destroy our custom window. We return TRUE to
* prevent the default delete-event handler, and destroy the window
* ourselves since webkit_web_inspector_close() only closes the
* inspector page — it doesn't destroy our custom GtkWindow. */
static gboolean on_inspector_window_delete(GtkWidget *widget,
GdkEvent *event,
gpointer user_data) {
(void)event;
WebKitWebInspector *insp = WEBKIT_WEB_INSPECTOR(user_data);
webkit_web_inspector_close(insp);
gtk_widget_destroy(widget);
return TRUE;
}
/* ::closed signal — the inspector page was closed (either by the user
* or programmatically). Reset our visibility flag. */
static void on_inspector_closed(WebKitWebInspector *inspector,
gpointer user_data) {
(void)inspector;
(void)user_data;
g_inspector_visible = FALSE;
}
/* ::open-window signal — create our own GtkWindow for the inspector
* instead of letting WebKit create its default window. This gives us
* full control over the window's title bar buttons and event handling.
* Return TRUE to prevent WebKit from creating its own window. */
static gboolean on_inspector_open_window(WebKitWebInspector *inspector,
gpointer user_data) {
(void)user_data;
GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
if (toplevel && GTK_IS_WINDOW(toplevel)) {
/* Restore saved geometry if we have it. */
const browser_settings_t *bs = settings_get();
if (bs->inspector_w > 0 && bs->inspector_h > 0) {
gtk_window_resize(GTK_WINDOW(toplevel),
bs->inspector_w, bs->inspector_h);
}
if (bs->inspector_x >= 0 && bs->inspector_y >= 0) {
gtk_window_move(GTK_WINDOW(toplevel),
bs->inspector_x, bs->inspector_y);
}
/* Track future changes. */
g_signal_connect(toplevel, "configure-event",
G_CALLBACK(on_inspector_window_configure), NULL);
}
}
static void on_inspector_detach(WebKitWebInspector *inspector,
gpointer user_data) {
(void)user_data;
WebKitWebViewBase *insp_view = webkit_web_inspector_get_web_view(inspector);
if (insp_view) {
GtkWidget *w = GTK_WIDGET(insp_view);
/* Hook realize to catch the detached window. */
g_signal_connect(w, "realize",
G_CALLBACK(on_inspector_webview_realize), NULL);
if (!insp_view) {
return FALSE; /* let WebKit handle it */
}
}
/* Track whether the inspector is currently shown for the active tab.
* We use a per-tab flag stored in a static hash (inspector state is
* per-webview, but we track it globally for the toggle). */
static gboolean g_inspector_visible = FALSE;
/* Create a new top-level window for the inspector. */
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Web Inspector");
/* Restore saved geometry if we have it. */
const browser_settings_t *bs = settings_get();
if (bs->inspector_w > 0 && bs->inspector_h > 0) {
gtk_window_set_default_size(GTK_WINDOW(window),
bs->inspector_w, bs->inspector_h);
} else {
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
}
/* Add the inspector's WebView to our window. */
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(insp_view));
/* Position the window if we have saved coordinates. */
if (bs->inspector_x >= 0 && bs->inspector_y >= 0) {
gtk_window_move(GTK_WINDOW(window), bs->inspector_x, bs->inspector_y);
}
/* Connect signals:
* - delete-event: route the X button through the inspector API
* - configure-event: persist window position/size
* - destroy: reset visibility flag as a fallback */
g_signal_connect(window, "delete-event",
G_CALLBACK(on_inspector_window_delete), inspector);
g_signal_connect(window, "configure-event",
G_CALLBACK(on_inspector_window_configure), NULL);
/* Show the window and all its children. */
gtk_widget_show_all(window);
/* Raise the window to the top. */
gtk_window_present(GTK_WINDOW(window));
return TRUE; /* prevent WebKit from creating its own window */
}
void tab_manager_toggle_inspector(void) {
tab_info_t *tab = tab_manager_get_active();
@@ -2104,8 +2149,10 @@ void tab_manager_toggle_inspector(void) {
* and persists for the webview's lifetime). We check a g_object data
* flag to avoid connecting multiple times. */
if (g_object_get_data(G_OBJECT(insp), "sovereign-inspector-hooked") == NULL) {
g_signal_connect(insp, "detach",
G_CALLBACK(on_inspector_detach), NULL);
g_signal_connect(insp, "open-window",
G_CALLBACK(on_inspector_open_window), NULL);
g_signal_connect(insp, "closed",
G_CALLBACK(on_inspector_closed), NULL);
g_object_set_data(G_OBJECT(insp), "sovereign-inspector-hooked",
GINT_TO_POINTER(1));
}
@@ -2114,6 +2161,11 @@ void tab_manager_toggle_inspector(void) {
webkit_web_inspector_close(insp);
g_inspector_visible = FALSE;
} else {
/* Force detach so the inspector opens in a separate window
* (triggers ::detach then ::open-window). */
if (webkit_web_inspector_get_can_attach(insp)) {
webkit_web_inspector_detach(insp);
}
webkit_web_inspector_show(insp);
g_inspector_visible = TRUE;
}
+2 -2
View File
@@ -11,9 +11,9 @@
#ifndef SOVEREIGN_BROWSER_VERSION_H
#define SOVEREIGN_BROWSER_VERSION_H
#define SB_VERSION "v0.0.69"
#define SB_VERSION "v0.0.70"
#define SB_VERSION_MAJOR 0
#define SB_VERSION_MINOR 0
#define SB_VERSION_PATCH 69
#define SB_VERSION_PATCH 70
#endif /* SOVEREIGN_BROWSER_VERSION_H */