detached 1f4013c

content: harden async popup callbacks

commit
1f4013cb0129941f616bf67b99a96b45c4d966e1
author
brice · · 2026-04-23 14:31 UTC
parent
5ef1bed
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +15 -6
M internal/infrastructure/cef/factory.go +9 -1
@@ -293,7 +293,15 @@ func (f *WebViewFactory) schedulePendingBrowserCreateRetry(
293293 w, h int32,
294294 pc *pendingBrowserCreate,
295295 ) {
296- if f == nil || wv == nil || pc == nil || wv.destroyed.Load() || pc.postTaskRetries > pendingBrowserCreateMaxPostRetries {
296+ if f == nil || wv == nil || pc == nil || wv.destroyed.Load() {
297+ return
298+ }
299+ if pc.postTaskRetries > pendingBrowserCreateMaxPostRetries {
300+ logging.FromContext(ctx).Warn().
301+ Uint64("webview_id", uint64(wv.id)).
302+ Int("retries", pc.postTaskRetries).
303+ Int("max_retries", pendingBrowserCreateMaxPostRetries).
304+ Msg("cef: browser creation retries exhausted")
297305 return
298306 }
299307 cefScheduleAfter(pendingBrowserCreateRetryDelay, func() {
@@ -293,7 +293,15 @@ func (f *WebViewFactory) schedulePendingBrowserCreateRetry(
293 w, h int32,293 w, h int32,
294 pc *pendingBrowserCreate,294 pc *pendingBrowserCreate,
295 ) {295 ) {
296- if f == nil || wv == nil || pc == nil || wv.destroyed.Load() || pc.postTaskRetries > pendingBrowserCreateMaxPostRetries {296+ if f == nil || wv == nil || pc == nil || wv.destroyed.Load() {
297+ return
298+ }
299+ if pc.postTaskRetries > pendingBrowserCreateMaxPostRetries {
300+ logging.FromContext(ctx).Warn().
301+ Uint64("webview_id", uint64(wv.id)).
302+ Int("retries", pc.postTaskRetries).
303+ Int("max_retries", pendingBrowserCreateMaxPostRetries).
304+ Msg("cef: browser creation retries exhausted")
297 return305 return
298 }306 }
299 cefScheduleAfter(pendingBrowserCreateRetryDelay, func() {307 cefScheduleAfter(pendingBrowserCreateRetryDelay, func() {
M internal/ui/coordinator/content/oauth_test.go +1 -0
@@ -246,6 +246,7 @@ func TestResumeParentPaneAfterOAuth_DifferentDomainFallsBackToReload(t *testing.
246246 webViews: map[entity.PaneID]port.WebView{
247247 parentPaneID: parentWV,
248248 },
249+ popups: newPopupManager(),
249250 }
250251
251252 c.resumeParentPaneAfterOAuth(context.Background(), parentPaneID, popupID, "https://www.notion.so/login", "https://accounts.example.com/callback?code=123")
@@ -246,6 +246,7 @@ func TestResumeParentPaneAfterOAuth_DifferentDomainFallsBackToReload(t *testing.
246 webViews: map[entity.PaneID]port.WebView{246 webViews: map[entity.PaneID]port.WebView{
247 parentPaneID: parentWV,247 parentPaneID: parentWV,
248 },248 },
249+ popups: newPopupManager(),
249 }250 }
250 251
251 c.resumeParentPaneAfterOAuth(context.Background(), parentPaneID, popupID, "https://www.notion.so/login", "https://accounts.example.com/callback?code=123")252 c.resumeParentPaneAfterOAuth(context.Background(), parentPaneID, popupID, "https://www.notion.so/login", "https://accounts.example.com/callback?code=123")
M internal/ui/coordinator/content/popup_manager.go +5 -5
@@ -354,7 +354,7 @@ func (pm *popupManager) createPopupWebView(
354354 }
355355
356356 log := logging.FromContext(ctx)
357- relatedErr := error(nil)
357+ var relatedErr error
358358
359359 if !noJavaScriptAccess && !pm.relatedPopupSupportDisabled() {
360360 popupWV, err := pm.factory.CreateRelated(ctx, parentID)
@@ -579,7 +579,7 @@ func (pm *popupManager) finishPopupCreate(
579579 if _, hasNativePopupLifecycle := create.PopupWebView.(port.PopupLifecycleCapable); !hasNativePopupLifecycle {
580580 if closeCapable, ok := create.PopupWebView.(port.OAuthCallbackCapable); ok {
581581 closeCapable.AddCloseCallback(func() {
582- pm.handlePopupClose(ctx, hooks, create.PopupID)
582+ pm.handlePopupClose(context.Background(), hooks, create.PopupID)
583583 })
584584 }
585585 }
@@ -608,10 +608,10 @@ func (pm *popupManager) finishPopupCreate(
608608
609609 if lifecycle, ok := create.PopupWebView.(port.PopupLifecycleCapable); ok {
610610 lifecycle.SetOnReadyToShow(func() {
611- pm.handlePopupReadyToShow(ctx, create.PopupID)
611+ pm.handlePopupReadyToShow(context.Background(), create.PopupID)
612612 })
613613 lifecycle.SetOnClose(func() {
614- pm.handlePopupClose(ctx, hooks, create.PopupID)
614+ pm.handlePopupClose(context.Background(), hooks, create.PopupID)
615615 })
616616 log.Info().
617617 Uint64("popup_id", uint64(create.PopupID)).
@@ -626,7 +626,7 @@ func (pm *popupManager) finishPopupCreate(
626626 Str("popup_type", create.PopupType.String()).
627627 Str("target_uri", logging.TruncateURL(create.Request.TargetURI, logURLMaxLen)).
628628 Msg("popup inserted, immediately ready (no PopupLifecycleCapable)")
629- pm.handlePopupReadyToShow(ctx, create.PopupID)
629+ pm.handlePopupReadyToShow(context.Background(), create.PopupID)
630630 }
631631
632632 return create.PopupWebView
@@ -354,7 +354,7 @@ func (pm *popupManager) createPopupWebView(
354 }354 }
355 355
356 log := logging.FromContext(ctx)356 log := logging.FromContext(ctx)
357- relatedErr := error(nil)357+ var relatedErr error
358 358
359 if !noJavaScriptAccess && !pm.relatedPopupSupportDisabled() {359 if !noJavaScriptAccess && !pm.relatedPopupSupportDisabled() {
360 popupWV, err := pm.factory.CreateRelated(ctx, parentID)360 popupWV, err := pm.factory.CreateRelated(ctx, parentID)
@@ -579,7 +579,7 @@ func (pm *popupManager) finishPopupCreate(
579 if _, hasNativePopupLifecycle := create.PopupWebView.(port.PopupLifecycleCapable); !hasNativePopupLifecycle {579 if _, hasNativePopupLifecycle := create.PopupWebView.(port.PopupLifecycleCapable); !hasNativePopupLifecycle {
580 if closeCapable, ok := create.PopupWebView.(port.OAuthCallbackCapable); ok {580 if closeCapable, ok := create.PopupWebView.(port.OAuthCallbackCapable); ok {
581 closeCapable.AddCloseCallback(func() {581 closeCapable.AddCloseCallback(func() {
582- pm.handlePopupClose(ctx, hooks, create.PopupID)582+ pm.handlePopupClose(context.Background(), hooks, create.PopupID)
583 })583 })
584 }584 }
585 }585 }
@@ -608,10 +608,10 @@ func (pm *popupManager) finishPopupCreate(
608 608
609 if lifecycle, ok := create.PopupWebView.(port.PopupLifecycleCapable); ok {609 if lifecycle, ok := create.PopupWebView.(port.PopupLifecycleCapable); ok {
610 lifecycle.SetOnReadyToShow(func() {610 lifecycle.SetOnReadyToShow(func() {
611- pm.handlePopupReadyToShow(ctx, create.PopupID)611+ pm.handlePopupReadyToShow(context.Background(), create.PopupID)
612 })612 })
613 lifecycle.SetOnClose(func() {613 lifecycle.SetOnClose(func() {
614- pm.handlePopupClose(ctx, hooks, create.PopupID)614+ pm.handlePopupClose(context.Background(), hooks, create.PopupID)
615 })615 })
616 log.Info().616 log.Info().
617 Uint64("popup_id", uint64(create.PopupID)).617 Uint64("popup_id", uint64(create.PopupID)).
@@ -626,7 +626,7 @@ func (pm *popupManager) finishPopupCreate(
626 Str("popup_type", create.PopupType.String()).626 Str("popup_type", create.PopupType.String()).
627 Str("target_uri", logging.TruncateURL(create.Request.TargetURI, logURLMaxLen)).627 Str("target_uri", logging.TruncateURL(create.Request.TargetURI, logURLMaxLen)).
628 Msg("popup inserted, immediately ready (no PopupLifecycleCapable)")628 Msg("popup inserted, immediately ready (no PopupLifecycleCapable)")
629- pm.handlePopupReadyToShow(ctx, create.PopupID)629+ pm.handlePopupReadyToShow(context.Background(), create.PopupID)
630 }630 }
631 631
632 return create.PopupWebView632 return create.PopupWebView

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