Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / widget / windows / nsWindowLoggedMessages.cpp
blobac0f05a8756205aa1646cf95c595b5486a04fbb8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <windef.h>
7 #include <winuser.h>
8 #include "mozilla/StaticPrefs_storage.h"
9 #include "mozilla/StaticPrefs_widget.h"
10 #include "nsWindowLoggedMessages.h"
11 #include "nsWindow.h"
12 #include "WinUtils.h"
13 #include <map>
14 #include <algorithm>
16 namespace mozilla::widget {
18 // NCCALCSIZE_PARAMS and WINDOWPOS are relatively large structures, so store
19 // them as a pointer to save memory
20 using NcCalcSizeVariantData =
21 Variant<UniquePtr<std::pair<NCCALCSIZE_PARAMS, WINDOWPOS>>, RECT>;
22 // to save memory, hold the raw data and only convert to string
23 // when requested
24 using MessageSpecificData =
25 Variant<std::pair<WPARAM, LPARAM>, // WM_SIZE, WM_MOVE
26 WINDOWPOS, // WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED
27 std::pair<WPARAM, RECT>, // WM_SIZING, WM_DPICHANGED, WM_MOVING
28 std::pair<WPARAM, nsString>, // WM_SETTINGCHANGE
29 std::pair<bool, NcCalcSizeVariantData>, // WM_NCCALCSIZE
30 MINMAXINFO // WM_GETMINMAXINFO
33 struct WindowMessageData {
34 long mEventCounter;
35 bool mIsPreEvent;
36 MessageSpecificData mSpecificData;
37 mozilla::Maybe<bool> mResult;
38 LRESULT mRetValue;
39 WindowMessageData(long eventCounter, bool isPreEvent,
40 MessageSpecificData&& specificData,
41 mozilla::Maybe<bool> result, LRESULT retValue)
42 : mEventCounter(eventCounter),
43 mIsPreEvent(isPreEvent),
44 mSpecificData(std::move(specificData)),
45 mResult(result),
46 mRetValue(retValue) {}
47 // Disallow copy constructor/operator since MessageSpecificData has a
48 // UniquePtr
49 WindowMessageData(const WindowMessageData&) = delete;
50 WindowMessageData& operator=(const WindowMessageData&) = delete;
51 WindowMessageData(WindowMessageData&&) = default;
52 WindowMessageData& operator=(WindowMessageData&&) = default;
55 struct WindowMessageDataSortKey {
56 long mEventCounter;
57 bool mIsPreEvent;
58 explicit WindowMessageDataSortKey(const WindowMessageData& data)
59 : mEventCounter(data.mEventCounter), mIsPreEvent(data.mIsPreEvent) {}
60 bool operator<(const WindowMessageDataSortKey& other) const {
61 if (mEventCounter < other.mEventCounter) {
62 return true;
64 if (other.mEventCounter < mEventCounter) {
65 return false;
67 if (mIsPreEvent && !other.mIsPreEvent) {
68 return true;
70 if (other.mIsPreEvent && !mIsPreEvent) {
71 return false;
73 // they're equal
74 return false;
78 struct CircularMessageBuffer {
79 // Only used when the vector is at its maximum size
80 size_t mNextFreeIndex = 0;
81 std::vector<WindowMessageData> mMessages;
83 static std::map<HWND, std::map<UINT, CircularMessageBuffer>> gWindowMessages;
85 static HWND GetHwndFromWidget(nsIWidget* windowWidget) {
86 nsWindow* window = static_cast<nsWindow*>(windowWidget);
87 return window->GetWindowHandle();
90 MessageSpecificData MakeMessageSpecificData(UINT event, WPARAM wParam,
91 LPARAM lParam) {
92 // Since we store this data for every message we log, make sure it's of a
93 // reasonable size. Keep in mind we're storing up to 10 (number of message
94 // types)
95 // * 6 (default number of messages per type to keep) of these messages per
96 // window.
97 static_assert(sizeof(MessageSpecificData) <= 48);
98 switch (event) {
99 case WM_SIZE:
100 case WM_MOVE:
101 return MessageSpecificData(std::make_pair(wParam, lParam));
102 case WM_WINDOWPOSCHANGING:
103 case WM_WINDOWPOSCHANGED: {
104 LPWINDOWPOS windowPosPtr = reinterpret_cast<LPWINDOWPOS>(lParam);
105 WINDOWPOS windowPos = *windowPosPtr;
106 return MessageSpecificData(std::move(windowPos));
108 case WM_SIZING:
109 case WM_DPICHANGED:
110 case WM_MOVING: {
111 LPRECT rectPtr = reinterpret_cast<LPRECT>(lParam);
112 RECT rect = *rectPtr;
113 return MessageSpecificData(std::make_pair(wParam, std::move(rect)));
115 case WM_SETTINGCHANGE: {
116 LPCWSTR wideStrPtr = reinterpret_cast<LPCWSTR>(lParam);
117 nsString str(wideStrPtr);
118 return MessageSpecificData(std::make_pair(wParam, std::move(str)));
120 case WM_NCCALCSIZE: {
121 bool shouldIndicateValidArea = wParam == TRUE;
122 if (shouldIndicateValidArea) {
123 LPNCCALCSIZE_PARAMS ncCalcSizeParamsPtr =
124 reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
125 NCCALCSIZE_PARAMS ncCalcSizeParams = *ncCalcSizeParamsPtr;
126 WINDOWPOS windowPos = *ncCalcSizeParams.lppos;
127 UniquePtr<std::pair<NCCALCSIZE_PARAMS, WINDOWPOS>> ncCalcSizeData =
128 MakeUnique<std::pair<NCCALCSIZE_PARAMS, WINDOWPOS>>(
129 std::pair(std::move(ncCalcSizeParams), std::move(windowPos)));
130 return MessageSpecificData(
131 std::make_pair(shouldIndicateValidArea,
132 NcCalcSizeVariantData(std::move(ncCalcSizeData))));
133 } else {
134 LPRECT rectPtr = reinterpret_cast<LPRECT>(lParam);
135 RECT rect = *rectPtr;
136 return MessageSpecificData(std::make_pair(
137 shouldIndicateValidArea, NcCalcSizeVariantData(std::move(rect))));
140 case WM_GETMINMAXINFO: {
141 PMINMAXINFO minMaxInfoPtr = reinterpret_cast<PMINMAXINFO>(lParam);
142 MINMAXINFO minMaxInfo = *minMaxInfoPtr;
143 return MessageSpecificData(std::move(minMaxInfo));
145 default:
146 MOZ_ASSERT_UNREACHABLE(
147 "Unhandled message type in MakeMessageSpecificData");
148 return MessageSpecificData(std::make_pair(wParam, lParam));
152 void AppendFriendlyMessageSpecificData(nsCString& str, UINT event,
153 bool isPreEvent,
154 const MessageSpecificData& data) {
155 switch (event) {
156 case WM_SIZE: {
157 const auto& params = data.as<std::pair<WPARAM, LPARAM>>();
158 nsAutoCString tempStr =
159 WmSizeParamInfo(params.first, params.second, isPreEvent);
160 str.AppendASCII(tempStr);
161 break;
163 case WM_MOVE: {
164 const auto& params = data.as<std::pair<WPARAM, LPARAM>>();
165 XLowWordYHighWordParamInfo(str, params.second, "upperLeft", isPreEvent);
166 break;
168 case WM_WINDOWPOSCHANGING:
169 case WM_WINDOWPOSCHANGED: {
170 const auto& params = data.as<WINDOWPOS>();
171 WindowPosParamInfo(str, reinterpret_cast<uint64_t>(&params),
172 "newSizeAndPos", isPreEvent);
173 break;
175 case WM_SIZING: {
176 const auto& params = data.as<std::pair<WPARAM, RECT>>();
177 WindowEdgeParamInfo(str, params.first, "edge", isPreEvent);
178 str.AppendASCII(" ");
179 RectParamInfo(str, reinterpret_cast<uint64_t>(&params.second), "rect",
180 isPreEvent);
181 break;
183 case WM_DPICHANGED: {
184 const auto& params = data.as<std::pair<WPARAM, RECT>>();
185 XLowWordYHighWordParamInfo(str, params.first, "newDPI", isPreEvent);
186 str.AppendASCII(" ");
187 RectParamInfo(str, reinterpret_cast<uint64_t>(&params.second),
188 "suggestedSizeAndPos", isPreEvent);
189 break;
191 case WM_MOVING: {
192 const auto& params = data.as<std::pair<WPARAM, RECT>>();
193 RectParamInfo(str, reinterpret_cast<uint64_t>(&params.second), "rect",
194 isPreEvent);
195 break;
197 case WM_SETTINGCHANGE: {
198 const auto& params = data.as<std::pair<WPARAM, nsString>>();
199 UiActionParamInfo(str, params.first, "uiAction", isPreEvent);
200 str.AppendASCII(" ");
201 WideStringParamInfo(
202 str,
203 reinterpret_cast<uint64_t>((const wchar_t*)(params.second.Data())),
204 "paramChanged", isPreEvent);
205 break;
207 case WM_NCCALCSIZE: {
208 const auto& params = data.as<std::pair<bool, NcCalcSizeVariantData>>();
209 bool shouldIndicateValidArea = params.first;
210 if (shouldIndicateValidArea) {
211 const auto& validAreaParams =
212 params.second
213 .as<UniquePtr<std::pair<NCCALCSIZE_PARAMS, WINDOWPOS>>>();
214 // Make pointer point to the cached data
215 validAreaParams->first.lppos = &validAreaParams->second;
216 nsAutoCString tempStr = WmNcCalcSizeParamInfo(
217 TRUE, reinterpret_cast<uint64_t>(&validAreaParams->first),
218 isPreEvent);
219 str.AppendASCII(tempStr);
220 } else {
221 RECT rect = params.second.as<RECT>();
222 nsAutoCString tempStr = WmNcCalcSizeParamInfo(
223 FALSE, reinterpret_cast<uint64_t>(&rect), isPreEvent);
224 str.AppendASCII(tempStr);
226 break;
228 case WM_GETMINMAXINFO: {
229 const auto& params = data.as<MINMAXINFO>();
230 MinMaxInfoParamInfo(str, reinterpret_cast<uint64_t>(&params), "",
231 isPreEvent);
232 break;
234 default:
235 MOZ_ASSERT(false,
236 "Unhandled message type in AppendFriendlyMessageSpecificData");
237 str.AppendASCII("???");
241 nsCString MakeFriendlyMessage(UINT event, bool isPreEvent, long eventCounter,
242 const MessageSpecificData& data,
243 mozilla::Maybe<bool> result, LRESULT retValue) {
244 nsCString str;
245 const char* eventName = mozilla::widget::WinUtils::WinEventToEventName(event);
246 MOZ_ASSERT(eventName, "Unknown event name in MakeFriendlyMessage");
247 eventName = eventName ? eventName : "(unknown)";
248 str.AppendPrintf("%6ld %04x (%s) - ", eventCounter, event, eventName);
249 AppendFriendlyMessageSpecificData(str, event, isPreEvent, data);
250 const char* resultMsg =
251 result.isSome() ? (result.value() ? "true" : "false") : "initial call";
252 str.AppendPrintf(" 0x%08llX (%s)",
253 result.isSome() ? static_cast<uint64_t>(retValue) : 0,
254 resultMsg);
255 return str;
258 void WindowClosed(HWND hwnd) { gWindowMessages.erase(hwnd); }
260 void LogWindowMessage(HWND hwnd, UINT event, bool isPreEvent, long eventCounter,
261 WPARAM wParam, LPARAM lParam, mozilla::Maybe<bool> result,
262 LRESULT retValue) {
263 auto& hwndMessages = gWindowMessages[hwnd];
264 auto& hwndWindowMessages = hwndMessages[event];
265 WindowMessageData messageData = {
266 eventCounter, isPreEvent, MakeMessageSpecificData(event, wParam, lParam),
267 result, retValue};
268 uint32_t numberOfMessagesToKeep =
269 StaticPrefs::widget_windows_messages_to_log();
270 if (hwndWindowMessages.mMessages.size() < numberOfMessagesToKeep) {
271 // haven't reached limit yet
272 hwndWindowMessages.mMessages.push_back(std::move(messageData));
273 } else {
274 hwndWindowMessages.mMessages[hwndWindowMessages.mNextFreeIndex] =
275 std::move(messageData);
277 hwndWindowMessages.mNextFreeIndex =
278 (hwndWindowMessages.mNextFreeIndex + 1) % numberOfMessagesToKeep;
281 void GetLatestWindowMessages(RefPtr<nsIWidget> windowWidget,
282 nsTArray<nsCString>& messages) {
283 HWND hwnd = GetHwndFromWidget(windowWidget);
284 const auto& rawMessages = gWindowMessages[hwnd];
285 nsTArray<std::pair<WindowMessageDataSortKey, nsCString>>
286 sortKeyAndMessageArray;
287 sortKeyAndMessageArray.SetCapacity(
288 rawMessages.size() * StaticPrefs::widget_windows_messages_to_log());
289 for (const auto& eventAndMessage : rawMessages) {
290 for (const auto& messageData : eventAndMessage.second.mMessages) {
291 nsCString message = MakeFriendlyMessage(
292 eventAndMessage.first, messageData.mIsPreEvent,
293 messageData.mEventCounter, messageData.mSpecificData,
294 messageData.mResult, messageData.mRetValue);
295 WindowMessageDataSortKey sortKey(messageData);
296 sortKeyAndMessageArray.AppendElement(
297 std::make_pair(sortKey, std::move(message)));
300 std::sort(sortKeyAndMessageArray.begin(), sortKeyAndMessageArray.end());
301 messages.SetCapacity(sortKeyAndMessageArray.Length());
302 for (const std::pair<WindowMessageDataSortKey, nsCString>& entry :
303 sortKeyAndMessageArray) {
304 messages.AppendElement(std::move(entry.second));
307 } // namespace mozilla::widget