diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index e00c2c13c1803..1fd49ff95c4b1 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -439,6 +439,12 @@ HWNDMessageHandler::~HWNDMessageHandler() { // Unhook MoveLoopMouseWatcher, to prevent call backs to this after deletion. MoveLoopMouseWatcher::UnhookForHost(this); + // Defense in depth: ensure no reference to `this` survives in the static + // fullscreen monitor map. OnDestroy() already calls this, but the map can + // hold duplicate entries if the window migrated monitors while fullscreen, + // and OnDestroy() may not run on every destruction path. + RemoveCurrentWindowFromFullscreenMonitorMap(); + // Clear pointer to this in `hwnd()`'s user data, to prevent installed hooks // from calling back into this after deletion. ClearUserData(); @@ -3891,12 +3897,16 @@ POINT HWNDMessageHandler::GetCursorPos() const { } void HWNDMessageHandler::RemoveCurrentWindowFromFullscreenMonitorMap() { - auto& map = fullscreen_monitor_map_.Get(); - const auto i = std::ranges::find( - map, this, &FullscreenWindowMonitorMap::value_type::second); - if (i != map.end()) { - map.erase(i); - } + // Erase *every* entry whose value is `this`. The map can transiently hold + // more than one entry for the same handler if the fullscreen window was + // migrated to a different monitor outside of SetFullscreen() (e.g. via + // Win+Shift+Arrow or after a monitor hot-unplug), because SetFullscreen() + // erases by current-monitor key rather than by value. Erasing only the + // first match would leave a dangling pointer in the static map after + // destruction, which CheckAndHandleBackgroundFullscreenOnMonitor() would + // later dereference. + std::erase_if(fullscreen_monitor_map_.Get(), + [this](const auto& kv) { return kv.second == this; }); } void HWNDMessageHandler::UpdateFullscreenMonitorMap() {