diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-11-17 13:52:52 +0800 |
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-11-17 13:52:52 +0800 |
| commit | f7c4d19df66c602d74795e98ce2ee4390d06fbb4 (patch) | |
| tree | 5d91dc18ae8129652d4c5b88234d0bb7b93dcf13 | |
| parent | c9f31440d65a867e2316e9a19ee3f5db2d0e53a7 (diff) | |
| download | cru-f7c4d19df66c602d74795e98ce2ee4390d06fbb4.tar.gz cru-f7c4d19df66c602d74795e98ce2ee4390d06fbb4.tar.bz2 cru-f7c4d19df66c602d74795e98ce2ee4390d06fbb4.zip | |
Harden delete after free.
| -rw-r--r-- | include/cru/ui/controls/Control.h | 13 | ||||
| -rw-r--r-- | include/cru/ui/controls/Window.h | 33 | ||||
| -rw-r--r-- | src/ui/controls/Control.cpp | 17 | ||||
| -rw-r--r-- | src/ui/controls/Window.cpp | 33 |
4 files changed, 73 insertions, 23 deletions
diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h index 684bc960..94de4cdc 100644 --- a/include/cru/ui/controls/Control.h +++ b/include/cru/ui/controls/Control.h @@ -1,12 +1,14 @@ #pragma once #include "../Base.h" #include "../DeleteLater.h" +#include "../events/KeyEventArgs.h" +#include "../events/MouseWheelEventArgs.h" #include "../events/UiEvents.h" +#include "../render/MeasureRequirement.h" #include "../render/RenderObject.h" #include "../style/StyleRuleSet.h" -#include "cru/ui/events/KeyEventArgs.h" -#include "cru/ui/events/MouseWheelEventArgs.h" -#include "cru/ui/render/MeasureRequirement.h" + +#include <cru/base/SelfResolvable.h> namespace cru::ui::controls { @@ -19,7 +21,9 @@ namespace cru::ui::controls { * - RemoveChild(Control* child) * The last two methods are totally for convenient control tree management. */ -class CRU_UI_API Control : public Object, public DeleteLaterImpl { +class CRU_UI_API Control : public Object, + public DeleteLaterImpl, + public SelfResolvable<Control> { friend class RootControl; CRU_DEFINE_CLASS_LOG_TAG("Control") @@ -41,6 +45,7 @@ class CRU_UI_API Control : public Object, public DeleteLaterImpl { Control* GetParent() const { return parent_; } void SetParent(Control* parent); + bool HasAncestor(Control* control); virtual void ForEachChild(const std::function<void(Control*)>& predicate) = 0; diff --git a/include/cru/ui/controls/Window.h b/include/cru/ui/controls/Window.h index e25694ba..88320219 100644 --- a/include/cru/ui/controls/Window.h +++ b/include/cru/ui/controls/Window.h @@ -13,6 +13,8 @@ namespace cru::ui::controls { class CRU_UI_API Window : public LayoutControl<render::StackLayoutRenderObject> { CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::Window") + friend Control; + public: static constexpr std::string_view kControlType = "Window"; @@ -58,7 +60,6 @@ class CRU_UI_API Window void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor); bool IsInEventHandling(); - void UpdateCursor(); CRU_DEFINE_EVENT(AfterLayout, std::nullptr_t) @@ -104,7 +105,8 @@ class CRU_UI_API Window if (original_sender == nullptr || original_sender == last_receiver) return; std::string log = "Begin dispatching routed event " + - (original_sender->*event_ptr)()->GetName() + ":\n\tTunnel:"; + (original_sender->*event_ptr)()->GetName() + + ":\n\tTunnel:"; Guard logging_guard([&] { log += "\nEnd dispatching routed event " + @@ -112,11 +114,11 @@ class CRU_UI_API Window CRU_LOG_TAG_DEBUG("{}", log); }); - std::vector<Control*> receive_list; + std::vector<ObjectResolver<Control>> receive_list; auto parent = original_sender; while (parent != last_receiver) { - receive_list.push_back(parent); + receive_list.push_back(parent->CreateResolver()); parent = parent->GetParent(); } @@ -124,8 +126,12 @@ class CRU_UI_API Window // tunnel for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { - auto control = *i; + auto control = i->Resolve(); log += " "; + if (!control) { + log += "(deleted)"; + continue; + } log += control->GetDebugId(); EventArgs event_args(control, original_sender, std::forward<Args>(args)...); @@ -140,8 +146,13 @@ class CRU_UI_API Window // bubble if (!handled) { log += "\n\tBubble:"; - for (auto control : receive_list) { + for (auto resolver : receive_list) { + auto control = resolver.Resolve(); log += " "; + if (!control) { + log += "(deleted)"; + continue; + } log += control->GetDebugId(); EventArgs event_args(control, original_sender, std::forward<Args>(args)...); @@ -155,8 +166,13 @@ class CRU_UI_API Window log += "\n\tDirect:"; // direct - for (auto control : receive_list) { + for (auto resolver : receive_list) { + auto control = resolver.Resolve(); log += " "; + if (!control) { + log += "(deleted)"; + continue; + } log += control->GetDebugId(); EventArgs event_args(control, original_sender, std::forward<Args>(args)...); @@ -164,6 +180,9 @@ class CRU_UI_API Window } } + void UpdateCursor(); + void NotifyControlDestroyed(Control* control); + private: int event_handling_count_; diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index e903b5b3..9c0fc537 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -1,4 +1,5 @@ #include "cru/ui/controls/Control.h" +#include "cru/base/log/Logger.h" #include "cru/ui/controls/Window.h" #include "cru/platform/gui/Cursor.h" @@ -27,11 +28,14 @@ Control::Control() { Control::~Control() { if (auto window = GetWindow()) { if (window->IsInEventHandling()) { - // Don't delete control during event handling. Use DeleteLater. - std::terminate(); + CRU_LOG_TAG_WARN( + "Better use delete later to delete control during event handling."); } } + if (auto window = GetWindow()) { + window->NotifyControlDestroyed(this); + } RemoveFromParent(); } @@ -58,6 +62,15 @@ void Control::SetParent(Control* parent) { OnParentChanged(old_parent, parent); } +bool Control::HasAncestor(Control* control) { + auto parent = this; + while (parent) { + if (parent == control) return true; + parent = parent->GetParent(); + } + return false; +} + void Control::RemoveFromParent() { if (parent_) { parent_->RemoveChild(this); diff --git a/src/ui/controls/Window.cpp b/src/ui/controls/Window.cpp index e6abc48d..c82b2485 100644 --- a/src/ui/controls/Window.cpp +++ b/src/ui/controls/Window.cpp @@ -265,16 +265,6 @@ void Window::SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor) { bool Window::IsInEventHandling() { return event_handling_count_; } -void Window::UpdateCursor() { - if (override_cursor_) { - native_window_->SetCursor(override_cursor_); - } else { - const auto capture = GetMouseCaptureControl(); - native_window_->SetCursor( - (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); - } -} - void Window::OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t) { CRU_UNUSED(window) @@ -428,4 +418,27 @@ void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, } } +void Window::UpdateCursor() { + if (override_cursor_) { + native_window_->SetCursor(override_cursor_); + } else { + const auto capture = GetMouseCaptureControl(); + native_window_->SetCursor( + (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); + } +} + +void Window::NotifyControlDestroyed(Control* control) { + if (focus_control_->HasAncestor(control)) { + focus_control_ = control->GetParent(); + } + + if (mouse_captured_control_->HasAncestor(control)) { + mouse_captured_control_ = control->GetParent(); + } + + if (mouse_hover_control_->HasAncestor(control)) { + mouse_hover_control_ = control->GetParent(); + } +} } // namespace cru::ui::controls |
