fix(cef): guard delayed viewport resize pulses
- commit
- 84b9d0cf28d2fd8e364f462378034f1b92663c6b
- author
- brice · · 2026-06-20 14:50 UTC
- parent
- e857952
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +48 -11
M internal/infrastructure/cef/webview_viewport.go +15 -9
| @@ -152,11 +152,11 @@ func (wv *WebView) scheduleResizeRepaintPulse(ctx context.Context, reason string | |||
| 152 | 152 | if wv == nil || wv.destroyed.Load() { | |
| 153 | 153 | return | |
| 154 | 154 | } | |
| 155 | - | visible := true | |
| 156 | - | if wv.viewBridge != nil { | |
| 157 | - | if widget := wv.viewBridge.Widget(); widget != nil { | |
| 158 | - | visible = widget.IsVisible() | |
| 159 | - | } | |
| 155 | + | wv.mu.RLock() | |
| 156 | + | host := wv.host | |
| 157 | + | wv.mu.RUnlock() | |
| 158 | + | if host == nil { | |
| 159 | + | return | |
| 160 | 160 | } | |
| 161 | 161 | seq := wv.viewportResizePulseSeq.Add(1) | |
| 162 | 162 | for _, delayMs := range [...]int64{16, 48} { | |
| @@ -165,16 +165,15 @@ func (wv *WebView) scheduleResizeRepaintPulse(ctx context.Context, reason string | |||
| 165 | 165 | return | |
| 166 | 166 | } | |
| 167 | 167 | wv.mu.RLock() | |
| 168 | - | host := wv.host | |
| 168 | + | currentHost := wv.host | |
| 169 | 169 | wv.mu.RUnlock() | |
| 170 | - | if host == nil { | |
| 170 | + | if currentHost != host { | |
| 171 | 171 | return | |
| 172 | 172 | } | |
| 173 | - | notifyBrowserViewportSync(host, visible) | |
| 173 | + | notifyBrowserViewportResize(host) | |
| 174 | 174 | logging.FromContext(ctx).Debug(). | |
| 175 | 175 | Uint64("webview_id", uint64(wv.id)). | |
| 176 | 176 | Int64("delay_ms", delayMs). | |
| 177 | - | Bool("visible", visible). | |
| 178 | 177 | Str("reason", reason). | |
| 179 | 178 | Msg("cef: delayed resize viewport sync") | |
| 180 | 179 | })) | |
| @@ -533,6 +532,13 @@ func notifyBrowserViewportSync(host viewportSyncBrowserHost, visible bool) { | |||
| 533 | 532 | if visible { | |
| 534 | 533 | host.WasHidden(0) | |
| 535 | 534 | } | |
| 535 | + | notifyBrowserViewportResize(host) | |
| 536 | + | } | |
| 537 | + | ||
| 538 | + | func notifyBrowserViewportResize(host viewportSyncBrowserHost) { | |
| 539 | + | if host == nil { | |
| 540 | + | return | |
| 541 | + | } | |
| 536 | 542 | host.NotifyScreenInfoChanged() | |
| 537 | 543 | notifyBrowserResize(host) | |
| 538 | 544 | } | |
| @@ -152,11 +152,11 @@ func (wv *WebView) scheduleResizeRepaintPulse(ctx context.Context, reason string | |||||
| 152 | if wv == nil || wv.destroyed.Load() { | 152 | if wv == nil || wv.destroyed.Load() { | ||
| 153 | return | 153 | return | ||
| 154 | } | 154 | } | ||
| 155 | - | visible := true | 155 | + | wv.mu.RLock() |
| 156 | - | if wv.viewBridge != nil { | 156 | + | host := wv.host |
| 157 | - | if widget := wv.viewBridge.Widget(); widget != nil { | 157 | + | wv.mu.RUnlock() |
| 158 | - | visible = widget.IsVisible() | 158 | + | if host == nil { |
| 159 | - | } | 159 | + | return |
| 160 | } | 160 | } | ||
| 161 | seq := wv.viewportResizePulseSeq.Add(1) | 161 | seq := wv.viewportResizePulseSeq.Add(1) | ||
| 162 | for _, delayMs := range [...]int64{16, 48} { | 162 | for _, delayMs := range [...]int64{16, 48} { | ||
| @@ -165,16 +165,15 @@ func (wv *WebView) scheduleResizeRepaintPulse(ctx context.Context, reason string | |||||
| 165 | return | 165 | return | ||
| 166 | } | 166 | } | ||
| 167 | wv.mu.RLock() | 167 | wv.mu.RLock() | ||
| 168 | - | host := wv.host | 168 | + | currentHost := wv.host |
| 169 | wv.mu.RUnlock() | 169 | wv.mu.RUnlock() | ||
| 170 | - | if host == nil { | 170 | + | if currentHost != host { |
| 171 | return | 171 | return | ||
| 172 | } | 172 | } | ||
| 173 | - | notifyBrowserViewportSync(host, visible) | 173 | + | notifyBrowserViewportResize(host) |
| 174 | logging.FromContext(ctx).Debug(). | 174 | logging.FromContext(ctx).Debug(). | ||
| 175 | Uint64("webview_id", uint64(wv.id)). | 175 | Uint64("webview_id", uint64(wv.id)). | ||
| 176 | Int64("delay_ms", delayMs). | 176 | Int64("delay_ms", delayMs). | ||
| 177 | - | Bool("visible", visible). | |||
| 178 | Str("reason", reason). | 177 | Str("reason", reason). | ||
| 179 | Msg("cef: delayed resize viewport sync") | 178 | Msg("cef: delayed resize viewport sync") | ||
| 180 | })) | 179 | })) | ||
| @@ -533,6 +532,13 @@ func notifyBrowserViewportSync(host viewportSyncBrowserHost, visible bool) { | |||||
| 533 | if visible { | 532 | if visible { | ||
| 534 | host.WasHidden(0) | 533 | host.WasHidden(0) | ||
| 535 | } | 534 | } | ||
| 535 | + | notifyBrowserViewportResize(host) | |||
| 536 | + | } | |||
| 537 | + | ||||
| 538 | + | func notifyBrowserViewportResize(host viewportSyncBrowserHost) { | |||
| 539 | + | if host == nil { | |||
| 540 | + | return | |||
| 541 | + | } | |||
| 536 | host.NotifyScreenInfoChanged() | 542 | host.NotifyScreenInfoChanged() | ||
| 537 | notifyBrowserResize(host) | 543 | notifyBrowserResize(host) | ||
| 538 | } | 544 | } | ||
M internal/infrastructure/cef/webview_viewport_test.go +33 -2
| @@ -451,13 +451,44 @@ func TestScheduleResizeRepaintPulse_CoalescesToLatestSequence(t *testing.T) { | |||
| 451 | 451 | scheduled[2].Execute() | |
| 452 | 452 | scheduled[3].Execute() | |
| 453 | 453 | require.Equal(t, []string{ | |
| 454 | - | "WasHidden", | |
| 455 | 454 | "NotifyScreenInfoChanged", | |
| 456 | 455 | "WasResized", | |
| 457 | 456 | "Invalidate", | |
| 458 | - | "WasHidden", | |
| 459 | 457 | "NotifyScreenInfoChanged", | |
| 460 | 458 | "WasResized", | |
| 461 | 459 | "Invalidate", | |
| 462 | 460 | }, host.calls) | |
| 463 | 461 | } | |
| 462 | + | ||
| 463 | + | func TestScheduleResizeRepaintPulse_SkipsStaleHost(t *testing.T) { | |
| 464 | + | oldNewTask := cefNewTask | |
| 465 | + | oldPostDelayedTask := cefPostDelayedTask | |
| 466 | + | defer func() { | |
| 467 | + | cefNewTask = oldNewTask | |
| 468 | + | cefPostDelayedTask = oldPostDelayedTask | |
| 469 | + | }() | |
| 470 | + | ||
| 471 | + | cefNewTask = func(task purecef.Task) purecef.Task { return task } | |
| 472 | + | ||
| 473 | + | var scheduled []purecef.Task | |
| 474 | + | cefPostDelayedTask = func(threadID purecef.ThreadID, task purecef.Task, delayMs int64) int32 { | |
| 475 | + | require.Equal(t, purecef.ThreadIDTidUi, threadID) | |
| 476 | + | require.NotNil(t, task) | |
| 477 | + | scheduled = append(scheduled, task) | |
| 478 | + | return 1 | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | capturedHost := &viewportSyncOrderHost{} | |
| 482 | + | currentHost := &viewportSyncOrderHost{} | |
| 483 | + | wv := &WebView{ctx: context.Background(), host: capturedHost} | |
| 484 | + | ||
| 485 | + | wv.scheduleResizeRepaintPulse(context.Background(), "stale-host") | |
| 486 | + | wv.host = currentHost | |
| 487 | + | ||
| 488 | + | require.Len(t, scheduled, 2) | |
| 489 | + | scheduled[0].Execute() | |
| 490 | + | scheduled[1].Execute() | |
| 491 | + | ||
| 492 | + | require.Empty(t, capturedHost.calls) | |
| 493 | + | require.Empty(t, currentHost.calls) | |
| 494 | + | } | |
| @@ -451,13 +451,44 @@ func TestScheduleResizeRepaintPulse_CoalescesToLatestSequence(t *testing.T) { | |||||
| 451 | scheduled[2].Execute() | 451 | scheduled[2].Execute() | ||
| 452 | scheduled[3].Execute() | 452 | scheduled[3].Execute() | ||
| 453 | require.Equal(t, []string{ | 453 | require.Equal(t, []string{ | ||
| 454 | - | "WasHidden", | |||
| 455 | "NotifyScreenInfoChanged", | 454 | "NotifyScreenInfoChanged", | ||
| 456 | "WasResized", | 455 | "WasResized", | ||
| 457 | "Invalidate", | 456 | "Invalidate", | ||
| 458 | - | "WasHidden", | |||
| 459 | "NotifyScreenInfoChanged", | 457 | "NotifyScreenInfoChanged", | ||
| 460 | "WasResized", | 458 | "WasResized", | ||
| 461 | "Invalidate", | 459 | "Invalidate", | ||
| 462 | }, host.calls) | 460 | }, host.calls) | ||
| 463 | } | 461 | } | ||
| 462 | + | ||||
| 463 | + | func TestScheduleResizeRepaintPulse_SkipsStaleHost(t *testing.T) { | |||
| 464 | + | oldNewTask := cefNewTask | |||
| 465 | + | oldPostDelayedTask := cefPostDelayedTask | |||
| 466 | + | defer func() { | |||
| 467 | + | cefNewTask = oldNewTask | |||
| 468 | + | cefPostDelayedTask = oldPostDelayedTask | |||
| 469 | + | }() | |||
| 470 | + | ||||
| 471 | + | cefNewTask = func(task purecef.Task) purecef.Task { return task } | |||
| 472 | + | ||||
| 473 | + | var scheduled []purecef.Task | |||
| 474 | + | cefPostDelayedTask = func(threadID purecef.ThreadID, task purecef.Task, delayMs int64) int32 { | |||
| 475 | + | require.Equal(t, purecef.ThreadIDTidUi, threadID) | |||
| 476 | + | require.NotNil(t, task) | |||
| 477 | + | scheduled = append(scheduled, task) | |||
| 478 | + | return 1 | |||
| 479 | + | } | |||
| 480 | + | ||||
| 481 | + | capturedHost := &viewportSyncOrderHost{} | |||
| 482 | + | currentHost := &viewportSyncOrderHost{} | |||
| 483 | + | wv := &WebView{ctx: context.Background(), host: capturedHost} | |||
| 484 | + | ||||
| 485 | + | wv.scheduleResizeRepaintPulse(context.Background(), "stale-host") | |||
| 486 | + | wv.host = currentHost | |||
| 487 | + | ||||
| 488 | + | require.Len(t, scheduled, 2) | |||
| 489 | + | scheduled[0].Execute() | |||
| 490 | + | scheduled[1].Execute() | |||
| 491 | + | ||||
| 492 | + | require.Empty(t, capturedHost.calls) | |||
| 493 | + | require.Empty(t, currentHost.calls) | |||
| 494 | + | } | |||