fix(macos): skip JS on hidden WebViews; re-layout main frame on wake#10812
fix(macos): skip JS on hidden WebViews; re-layout main frame on wake#10812adele-with-a-b wants to merge 1 commit into
Conversation
|
Heads-up: this build failure isn't from this PR. The Assimp bundles an old zlib ( |
Two related defects combined to produce a minutes-long frozen-input state after sleep/wake on macOS, requiring force-reboot to recover. 1. WebView::RunScript evaluated JavaScript into every wxWebView it was asked to, regardless of visibility. The home panel creates a pool of six wxWebView instances (left nav, right/home, makerworld, makerlab, print history, wiki) plus several more outside the home tab -- most stay Hide()'d and are swapped in via SetWebviewShow. The 2s m_LoginUpdateTimer (WebViewPanel::OnFreshLoginStatus) keeps pushing JS into them via ShowUserPrintTask -> SetLeftMenuShow -> WebView::RunScript(m_browserLeft, ...). On macOS each call becomes WKWebView evaluateJavaScript, which wakes the WebContent child (ProcessThrottler logs "probable wakeup reason: WebPageProxy::runJavaScriptInFrameInScriptWorld"), cancels suspension, and fails markAllLayersVolatile. After sleep/wake the extra layer-suspension churn compounds and the window's hit-test view ends up setHidden:YES while the window still has focus -- input gets dropped silently. Gate RunScript on webView->IsShownOnScreen(), wrapped in __WXMAC__ so non-macOS builds (where ICoreWebView2::ExecuteScript and webkit_web_view_run_javascript don't exhibit the same hidden-host-view failure mode) keep their existing behavior. The null-check stays unconditional as a defensive guard. The skip returns true (not false) so callers that branch on the return value (e.g. fila_manager SendMsg, which logs an error on false) don't see a benign hidden-view skip as a real failure. Matches the existing IsShownOnScreen pattern at WebViewDialog.cpp:763. 2. MacPowerCallBack on kIOMessageSystemHasPoweredOn only restored the selected printer; it never nudged the main-frame layout. Queue a Layout()/Refresh()/Update() on CallAfter so wxWidgets reconciles with AppKit and any stuck setHidden:YES view is refreshed out of its hidden state. CallAfter (rather than direct call) lets the IOKit notification handler unwind first; a direct call would also work since IONotificationPortGetRunLoopSource attaches to CFRunLoopGetCurrent() (the main thread's run loop). Known follow-up not addressed here: the IsShownOnScreen() gate causes some timer-driven JS pushes to drop on the floor while the user is on a non-Home tab (e.g. login-status refresh into m_browserLeft while the user is on Prepare/Preview). The home page already lazy- initializes most state on tab activation, so this is mostly cosmetic, but a future commit could add the m_has_pending_* defer/replay pattern used by SendDesignStaffpick at WebViewDialog.cpp:763. Tested against: v02.06.01.55 (Dev), macOS 26.4.1 arm64 (M5 Max). Author has run the patched Dev build for several days across multiple sleep/wake cycles without seeing the freeze recur.
842c360 to
4a9a6fa
Compare
| // most state on tab activation, so this is mostly cosmetic, but a | ||
| // future commit could add the same m_has_pending_* defer/replay | ||
| // pattern used by SendDesignStaffpick at WebViewDialog.cpp:763. | ||
| if (!webView->IsShownOnScreen()) |
There was a problem hiding this comment.
Is it better to move this check to the top of the function? otherwise we may get a message saying Running JavaScript but actually we wont running it. small issue:)
| BOOST_LOG_TRIVIAL(info) << "MacPowerCallBack: re-laying out main frame after wake"; | ||
| mf->Layout(); | ||
| mf->Refresh(); | ||
| mf->Update(); |
There was a problem hiding this comment.
Is mf->Update necessary here? what about remove this and let it update on next time when the framework think its a good time.
Summary
After macOS sleep/wake, BambuStudio's window can enter a state where it has focus but keyboard/mouse input is silently dropped — the user sees a "frozen" window for minutes, with force-reboot the only reliable recovery. Two related defects combine to produce this:
WebView::RunScriptevaluates JS into hidden webviews. The home panel keeps a pool of ~6 wxWebView instances (left nav, right/home, makerworld, makerlab, print history, wiki) that getHide()'d and swapped in viaSetWebviewShow. The 2sm_LoginUpdateTimer(WebViewPanel::OnFreshLoginStatus) keeps pushing JS into them. On macOS, each call becomesWKWebView evaluateJavaScript, which wakes the WebContent child process viarunJavaScriptInFrameInScriptWorld, cancelsProcessThrottlersuspension, and failsmarkAllLayersVolatile. After sleep/wake the layer-suspension churn compounds until the window's hit-test view ends upsetHidden:YESwhile the window still has focus. Input events route to a hidden NSView and get silently dropped.Fix: gate
RunScriptonwebView->IsShownOnScreen(), scoped via#ifdef __WXMAC__so non-macOS builds (whereICoreWebView2::ExecuteScriptandwebkit_web_view_run_javascriptdon't have an out-of-process content renderer with the same throttler-vs-hidden-host-view interaction) keep their existing behavior. Returnstruefor the skip case (matching the macOS path's existing semantics) so callers that branch on the return value don't conflate a benign hidden-view skip with a real script failure. SameIsShownOnScreen()precedent already atWebViewDialog.cpp:763(SendDesignStaffpick).MacPowerCallBackdoesn't re-layout on wake.kIOMessageSystemHasPoweredOnonly restores the previously-selected printer; nothing nudges the wxWidgets <-> AppKit visibility reconciliation. Any view left with a stalesetHidden:YESfrom defect 1 stays hidden indefinitely.Fix: schedule
Layout()/Refresh()/Update()on the main frame viawxGetApp().CallAfterso wxWidgets reconciles with AppKit after the IOKit notification handler unwinds. TheCallAfteris structurally unnecessary (the IOKit notification is dispatched on the main run loop viaIONotificationPortGetRunLoopSource(CFRunLoopGetCurrent(), ...)), but it's defensive: lets the system-power notification finish its own cleanup before we touch wx state.Reproduction
pmset sleepnow).markAllLayersVolatilefailures to accumulate).Without patch: keyboard/mouse input dropped silently. Console shows
WKWebView_evaluateJavaScriptwakeups every 2 s during sleep, plus repeatedPageClientImpl: window visible 1, view hidden 1, window occluded 0traces during the stuck-focus window. Eventually requires force-reboot.With patch: input continues to work after wake; no frozen state observed across multiple sleep/wake cycles over several days.
Test plan
IsShownOnScreen()gate causes some timer-driven JS pushes to drop on the floor while the user is on a non-Home tab. The home page already lazy-initializes most state on tab activation, so this is mostly cosmetic, but a future commit could add them_has_pending_*defer/replay pattern used bySendDesignStaffpickatWebViewDialog.cpp:763. Out of scope for this PR.)Tested
__WXMAC__scoping leaves their behavior unchanged. The unconditional null-check is added defensive code only.