Skip to content

fix(macos): skip JS on hidden WebViews; re-layout main frame on wake#10812

Open
adele-with-a-b wants to merge 1 commit into
bambulab:masterfrom
adele-with-a-b:fix/macos-wake-input-freeze
Open

fix(macos): skip JS on hidden WebViews; re-layout main frame on wake#10812
adele-with-a-b wants to merge 1 commit into
bambulab:masterfrom
adele-with-a-b:fix/macos-wake-input-freeze

Conversation

@adele-with-a-b

@adele-with-a-b adele-with-a-b commented May 20, 2026

Copy link
Copy Markdown
Contributor

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:

  1. WebView::RunScript evaluates JS into hidden webviews. The home panel keeps a pool of ~6 wxWebView instances (left nav, right/home, makerworld, makerlab, print history, wiki) that get Hide()'d and swapped in via SetWebviewShow. The 2s m_LoginUpdateTimer (WebViewPanel::OnFreshLoginStatus) keeps pushing JS into them. On macOS, each call becomes WKWebView evaluateJavaScript, which wakes the WebContent child process via runJavaScriptInFrameInScriptWorld, cancels ProcessThrottler suspension, and fails markAllLayersVolatile. After sleep/wake the layer-suspension churn compounds until the window's hit-test view ends up setHidden:YES while the window still has focus. Input events route to a hidden NSView and get silently dropped.

    Fix: gate RunScript on webView->IsShownOnScreen(), scoped via #ifdef __WXMAC__ so non-macOS builds (where ICoreWebView2::ExecuteScript and webkit_web_view_run_javascript don't have an out-of-process content renderer with the same throttler-vs-hidden-host-view interaction) keep their existing behavior. Returns true for 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. Same IsShownOnScreen() precedent already at WebViewDialog.cpp:763 (SendDesignStaffpick).

  2. MacPowerCallBack doesn't re-layout on wake. kIOMessageSystemHasPoweredOn only restores the previously-selected printer; nothing nudges the wxWidgets <-> AppKit visibility reconciliation. Any view left with a stale setHidden:YES from defect 1 stays hidden indefinitely.

    Fix: schedule Layout()/Refresh()/Update() on the main frame via wxGetApp().CallAfter so wxWidgets reconciles with AppKit after the IOKit notification handler unwinds. The CallAfter is structurally unnecessary (the IOKit notification is dispatched on the main run loop via IONotificationPortGetRunLoopSource(CFRunLoopGetCurrent(), ...)), but it's defensive: lets the system-power notification finish its own cleanup before we touch wx state.

Reproduction

  1. Launch the BambuStudio Dev build on macOS.
  2. Use the app normally for a few minutes (any tab; doesn't have to be Home).
  3. Close the laptop lid (or pmset sleepnow).
  4. Wait several minutes (long enough for markAllLayersVolatile failures to accumulate).
  5. Wake the machine, focus the BambuStudio window.

Without patch: keyboard/mouse input dropped silently. Console shows WKWebView_evaluateJavaScript wakeups every 2 s during sleep, plus repeated PageClientImpl: window visible 1, view hidden 1, window occluded 0 traces 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

  • Smoke-test sleep/wake in the patched Dev build over multiple cycles. Author has run this for several days without recurrence.
  • Smoke-test home-panel state staleness: switch to Prepare for ~30s with a login-status change happening (sign out via web), switch back to Home, confirm UI reflects current state. (Known follow-up: 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 the m_has_pending_* defer/replay pattern used by SendDesignStaffpick at WebViewDialog.cpp:763. Out of scope for this PR.)

Tested

  • macOS 26.4.1 / Apple Silicon (M5 Max) / Xcode 26.5 SDK / BambuStudio v02.06.01.55-Dev. Multiple sleep/wake cycles over several days, no recurrence.
  • Windows / Linux: not exercised, but the __WXMAC__ scoping leaves their behavior unchanged. The unconditional null-check is added defensive code only.

@BambulabRobot BambulabRobot requested a review from tonghao-bbl May 26, 2026 06:53
@BenJule

BenJule commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Heads-up: this build failure isn't from this PR. The Build All (macos-*) / Build Deps jobs fail while building dep_Assimp, not on your changes.

Assimp bundles an old zlib (contrib/zlib) whose K&R-style function declarations no longer parse against the macOS 15+/26 SDK, so the deps build currently breaks on every macOS PR. A one-line fix is proposed in #11051 (use the system zlib for Assimp on macOS/Linux). Once it lands, a rebase or re-run should make this go green.

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.
@adele-with-a-b adele-with-a-b force-pushed the fix/macos-wake-input-freeze branch from 842c360 to 4a9a6fa Compare June 9, 2026 02:46
// 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())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is mf->Update necessary here? what about remove this and let it update on next time when the framework think its a good time.

@tonghao-bbl tonghao-bbl added more_info_needed Waiting response Waiting more information from user and removed more_info_needed labels Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Waiting response Waiting more information from user

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants