fix (WeaselUI): Crash when the Weasel Panel creation is re-entried.#1839
fix (WeaselUI): Crash when the Weasel Panel creation is re-entried.#1839cqjjjzr wants to merge 1 commit intorime:masterfrom
Conversation
Do not create UI::pimpl_->panel when the previous one is still alive.
There was a problem hiding this comment.
Pull request overview
This PR makes weasel::UI::Create(HWND parent) idempotent to prevent crashes/assertions when panel creation is re-entered (e.g., Thunderbird composition edge cases), by reusing an existing WeaselPanel instead of attempting to recreate it.
Changes:
- Lazily allocate
UIImplonly when needed. - Early-return from
UI::Createwhen the panel window already exists. - Return a success/failure result based on the underlying
WeaselPanel::Createcall.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (pimpl_->panel.IsWindow()) | ||
| return true; |
There was a problem hiding this comment.
Create(HWND parent) now returns early when panel.IsWindow() is true, which makes the parent argument effectively ignored on re-entry. If callers can re-enter Create() with a different parent (e.g., focus moved to a different active window), the panel will remain owned by the old window, which can break z-order/lifetime semantics for a popup. Consider checking the current owner (GetParent() for a WS_POPUP owned window) and, when it differs from parent, either update the owner (e.g., via SetWindowLongPtr(GWLP_HWNDPARENT, ...)) or destroy/recreate the panel with the new parent.
| if (pimpl_->panel.IsWindow()) | |
| return true; | |
| if (pimpl_->panel.IsWindow()) { | |
| HWND current_parent = ::GetParent(pimpl_->panel.m_hWnd); | |
| if (current_parent != parent) { | |
| ::SetLastError(0); | |
| LONG_PTR previous_parent = ::SetWindowLongPtr( | |
| pimpl_->panel.m_hWnd, GWLP_HWNDPARENT, | |
| reinterpret_cast<LONG_PTR>(parent)); | |
| if (previous_parent == 0 && ::GetLastError() != 0) | |
| return false; | |
| } | |
| return true; | |
| } |
| pimpl_ = new UIImpl(*this); | ||
| if (!pimpl_) | ||
| return false; | ||
| } |
There was a problem hiding this comment.
new UIImpl(*this) will typically throw std::bad_alloc on failure (or terminate if exceptions are disabled), so the subsequent if (!pimpl_) return false; is ineffective/misleading unless you intentionally use std::nothrow. Consider either switching to new (std::nothrow) UIImpl(*this) and keeping the null-check, or removing the null-check and handling allocation failure via exception policy for this project.
尝试修复如下问题:打开 Thunderbird 的回复窗口后,如果 Thunderbird 安装了 Reply With Header 扩展,那么由于目前疑似 Thunderbird 的问题(我目前开在了 https://bugzilla.mozilla.org/show_bug.cgi?id=2031427 ),在一些条件下
StartComposition调用会返回E_FAIL。然后因为一些未知原因UI::Create会在未销毁之前就被调用。在 Debug 下这会造成 Assertion Fail,在 Release 下似乎会造成后续的空指针解引用(具体位置没有特别定位到,因为测的时候没带调试 PDB)带崩整个 Thunderbird。
此 commit 尝试把
UI::Create改成幂等的,目前是复用了一下现有的 Panel,或许也应该考虑一下先销毁再创建新的,如果有必要例如更换 parent 等。但至少不应该出现输入法带崩整个应用程序的情况。