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/. */
8 #include "mozilla/StaticPrefs_storage.h"
9 #include "mozilla/StaticPrefs_widget.h"
10 #include "nsWindowLoggedMessages.h"
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
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
{
36 MessageSpecificData mSpecificData
;
37 mozilla::Maybe
<bool> mResult
;
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
)),
46 mRetValue(retValue
) {}
47 // Disallow copy constructor/operator since MessageSpecificData has a
49 WindowMessageData(const WindowMessageData
&) = delete;
50 WindowMessageData
& operator=(const WindowMessageData
&) = delete;
51 WindowMessageData(WindowMessageData
&&) = default;
52 WindowMessageData
& operator=(WindowMessageData
&&) = default;
55 struct WindowMessageDataSortKey
{
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
) {
64 if (other
.mEventCounter
< mEventCounter
) {
67 if (mIsPreEvent
&& !other
.mIsPreEvent
) {
70 if (other
.mIsPreEvent
&& !mIsPreEvent
) {
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
,
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
95 // * 6 (default number of messages per type to keep) of these messages per
97 static_assert(sizeof(MessageSpecificData
) <= 48);
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
));
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
))));
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
));
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
,
154 const MessageSpecificData
& data
) {
157 const auto& params
= data
.as
<std::pair
<WPARAM
, LPARAM
>>();
158 nsAutoCString tempStr
=
159 WmSizeParamInfo(params
.first
, params
.second
, isPreEvent
);
160 str
.AppendASCII(tempStr
);
164 const auto& params
= data
.as
<std::pair
<WPARAM
, LPARAM
>>();
165 XLowWordYHighWordParamInfo(str
, params
.second
, "upperLeft", isPreEvent
);
168 case WM_WINDOWPOSCHANGING
:
169 case WM_WINDOWPOSCHANGED
: {
170 const auto& params
= data
.as
<WINDOWPOS
>();
171 WindowPosParamInfo(str
, reinterpret_cast<uint64_t>(¶ms
),
172 "newSizeAndPos", isPreEvent
);
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>(¶ms
.second
), "rect",
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>(¶ms
.second
),
188 "suggestedSizeAndPos", isPreEvent
);
192 const auto& params
= data
.as
<std::pair
<WPARAM
, RECT
>>();
193 RectParamInfo(str
, reinterpret_cast<uint64_t>(¶ms
.second
), "rect",
197 case WM_SETTINGCHANGE
: {
198 const auto& params
= data
.as
<std::pair
<WPARAM
, nsString
>>();
199 UiActionParamInfo(str
, params
.first
, "uiAction", isPreEvent
);
200 str
.AppendASCII(" ");
203 reinterpret_cast<uint64_t>((const wchar_t*)(params
.second
.Data())),
204 "paramChanged", isPreEvent
);
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
=
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
),
219 str
.AppendASCII(tempStr
);
221 RECT rect
= params
.second
.as
<RECT
>();
222 nsAutoCString tempStr
= WmNcCalcSizeParamInfo(
223 FALSE
, reinterpret_cast<uint64_t>(&rect
), isPreEvent
);
224 str
.AppendASCII(tempStr
);
228 case WM_GETMINMAXINFO
: {
229 const auto& params
= data
.as
<MINMAXINFO
>();
230 MinMaxInfoParamInfo(str
, reinterpret_cast<uint64_t>(¶ms
), "",
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
) {
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,
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
,
263 auto& hwndMessages
= gWindowMessages
[hwnd
];
264 auto& hwndWindowMessages
= hwndMessages
[event
];
265 WindowMessageData messageData
= {
266 eventCounter
, isPreEvent
, MakeMessageSpecificData(event
, wParam
, lParam
),
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
));
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