v0.0.70 - Fix: WebKit inspector window X button not closing — handle open-window signal with custom GtkWindow
This commit is contained in:
Executable
+111
@@ -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
|
||||
+85
-33
@@ -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)) {
|
||||
|
||||
WebKitWebViewBase *insp_view = webkit_web_inspector_get_web_view(inspector);
|
||||
if (!insp_view) {
|
||||
return FALSE; /* let WebKit handle it */
|
||||
}
|
||||
|
||||
/* 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_resize(GTK_WINDOW(toplevel),
|
||||
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(toplevel),
|
||||
bs->inspector_x, bs->inspector_y);
|
||||
gtk_window_move(GTK_WINDOW(window), bs->inspector_x, bs->inspector_y);
|
||||
}
|
||||
/* Track future changes. */
|
||||
g_signal_connect(toplevel, "configure-event",
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* Show the window and all its children. */
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
/* 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;
|
||||
/* 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
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user