detached ee7cb2f

fix(webkit): skip TerminateWebProcess for related popup WebViews

commit
ee7cb2fef0f628a076cb18244a37f8d4dafad948
author
bnema · · 2025-12-27 17:07 UTC
committer
bnema · · 2025-12-27 17:12 UTC
parent
e5a8cf8
signature
GPG Unverified2B82207D370127A0 The key is unknown
2 files · +11 -2
M CHANGELOG.md +1 -0
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
1111 - New config: `workspace.switch_to_tab_on_move` (default: true).
1212
1313 ### Fixed
14+- **OAuth popup login**: Fixed parent page going blank after OAuth popup closes (e.g., Google login on claude.ai, notion.com). Related WebViews share a web process with their parent; destroying the popup was terminating the shared process, killing the parent. Now skips `TerminateWebProcess()` for popup WebViews.
1415 - Startup: defer WebView pool prewarm until after initial tab creation to reduce cold-start navigation latency.
1516 - README CLI examples: updated to match current commands/flags (`purge --force` only, `logs` usage, `sessions list --limit`, removed deprecated `--dmenu` root flag).
1617
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
11 - New config: `workspace.switch_to_tab_on_move` (default: true).11 - New config: `workspace.switch_to_tab_on_move` (default: true).
12 12
13 ### Fixed13 ### Fixed
14+- **OAuth popup login**: Fixed parent page going blank after OAuth popup closes (e.g., Google login on claude.ai, notion.com). Related WebViews share a web process with their parent; destroying the popup was terminating the shared process, killing the parent. Now skips `TerminateWebProcess()` for popup WebViews.
14 - Startup: defer WebView pool prewarm until after initial tab creation to reduce cold-start navigation latency.15 - Startup: defer WebView pool prewarm until after initial tab creation to reduce cold-start navigation latency.
15 - README CLI examples: updated to match current commands/flags (`purge --force` only, `logs` usage, `sessions list --limit`, removed deprecated `--dmenu` root flag).16 - README CLI examples: updated to match current commands/flags (`purge --force` only, `logs` usage, `sessions list --limit`, removed deprecated `--dmenu` root flag).
16 17
M internal/infrastructure/webkit/webview.go +10 -2
@@ -88,6 +88,7 @@ type WebView struct {
8888
8989 // State (protected by mutex)
9090 destroyed atomic.Bool
91+ isRelated bool // true if created via NewWebViewWithRelated (shares web process with parent)
9192 uri string
9293 title string
9394 progress float64
@@ -294,6 +295,7 @@ func NewWebViewWithRelated(ctx context.Context, parent *WebView, settings *Setti
294295
295296 wv := &WebView{
296297 inner: inner,
298+ isRelated: true, // Shares web process with parent - must not terminate process on destroy
297299 ucm: inner.GetUserContentManager(),
298300 logger: log.With().Str("component", "webview-popup").Logger(),
299301 signalIDs: make([]uint32, 0, 6),
@@ -981,7 +983,9 @@ func (wv *WebView) Destroy() {
981983
982984 // 5. Terminate the web process to free GPU resources (VA-API, DMA-BUF, GL contexts)
983985 // This is critical to prevent zombie processes that hold video decoder resources.
984- if wv.inner != nil {
986+ // IMPORTANT: Skip for related views (popups) - they share the web process with their
987+ // parent. Terminating the shared process would kill the parent WebView too!
988+ if !wv.isRelated && wv.inner != nil {
985989 wv.inner.TerminateWebProcess()
986990 }
987991
@@ -993,7 +997,11 @@ func (wv *WebView) Destroy() {
993997 wv.ucm = nil
994998 wv.findController = nil
995999
996- wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("webview destroyed and web process terminated")
1000+ if wv.isRelated {
1001+ wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("related webview destroyed (process shared with parent)")
1002+ } else {
1003+ wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("webview destroyed and web process terminated")
1004+ }
9971005 }
9981006
9991007 // RunJavaScript executes script in the specified world (empty for main world).
@@ -88,6 +88,7 @@ type WebView struct {
88 88
89 // State (protected by mutex)89 // State (protected by mutex)
90 destroyed atomic.Bool90 destroyed atomic.Bool
91+ isRelated bool // true if created via NewWebViewWithRelated (shares web process with parent)
91 uri string92 uri string
92 title string93 title string
93 progress float6494 progress float64
@@ -294,6 +295,7 @@ func NewWebViewWithRelated(ctx context.Context, parent *WebView, settings *Setti
294 295
295 wv := &WebView{296 wv := &WebView{
296 inner: inner,297 inner: inner,
298+ isRelated: true, // Shares web process with parent - must not terminate process on destroy
297 ucm: inner.GetUserContentManager(),299 ucm: inner.GetUserContentManager(),
298 logger: log.With().Str("component", "webview-popup").Logger(),300 logger: log.With().Str("component", "webview-popup").Logger(),
299 signalIDs: make([]uint32, 0, 6),301 signalIDs: make([]uint32, 0, 6),
@@ -981,7 +983,9 @@ func (wv *WebView) Destroy() {
981 983
982 // 5. Terminate the web process to free GPU resources (VA-API, DMA-BUF, GL contexts)984 // 5. Terminate the web process to free GPU resources (VA-API, DMA-BUF, GL contexts)
983 // This is critical to prevent zombie processes that hold video decoder resources.985 // This is critical to prevent zombie processes that hold video decoder resources.
984- if wv.inner != nil {986+ // IMPORTANT: Skip for related views (popups) - they share the web process with their
987+ // parent. Terminating the shared process would kill the parent WebView too!
988+ if !wv.isRelated && wv.inner != nil {
985 wv.inner.TerminateWebProcess()989 wv.inner.TerminateWebProcess()
986 }990 }
987 991
@@ -993,7 +997,11 @@ func (wv *WebView) Destroy() {
993 wv.ucm = nil997 wv.ucm = nil
994 wv.findController = nil998 wv.findController = nil
995 999
996- wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("webview destroyed and web process terminated")1000+ if wv.isRelated {
1001+ wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("related webview destroyed (process shared with parent)")
1002+ } else {
1003+ wv.logger.Debug().Uint64("id", uint64(wv.id)).Msg("webview destroyed and web process terminated")
1004+ }
997 }1005 }
998 1006
999 // RunJavaScript executes script in the specified world (empty for main world).1007 // RunJavaScript executes script in the specified world (empty for main world).

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close