Bug 1826326 [wpt PR 39360] - Get rid of ResourceWarnings produced by the tests, a...
[gecko.git] / widget / windows / WinUtils.cpp
blobcadf34af04fafb758c03f17482e65f79905985a8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WinUtils.h"
9 #include <knownfolders.h>
10 #include <winioctl.h>
12 #include "gfxPlatform.h"
13 #include "gfxUtils.h"
14 #include "nsWindow.h"
15 #include "nsWindowDefs.h"
16 #include "InputDeviceUtils.h"
17 #include "KeyboardLayout.h"
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/BackgroundHangMonitor.h"
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/dom/MouseEventBinding.h"
22 #include "mozilla/gfx/2D.h"
23 #include "mozilla/gfx/DataSurfaceHelpers.h"
24 #include "mozilla/gfx/DisplayConfigWindows.h"
25 #include "mozilla/gfx/Logging.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/ProfilerThreadSleep.h"
28 #include "mozilla/RefPtr.h"
29 #include "mozilla/SchedulerGroup.h"
30 #include "mozilla/WinHeaderOnlyUtils.h"
31 #include "mozilla/WindowsVersion.h"
32 #include "mozilla/Unused.h"
33 #include "nsIContentPolicy.h"
34 #include "WindowsUIUtils.h"
35 #include "nsContentUtils.h"
37 #include "mozilla/Logging.h"
39 #include "nsString.h"
40 #include "nsDirectoryServiceUtils.h"
41 #include "imgIContainer.h"
42 #include "imgITools.h"
43 #include "nsNetUtil.h"
44 #include "nsIOutputStream.h"
45 #include "nsNetCID.h"
46 #include "prtime.h"
47 #ifdef MOZ_PLACES
48 # include "nsIFaviconService.h"
49 #endif
50 #include "nsIDownloader.h"
51 #include "nsIChannel.h"
52 #include "nsIThread.h"
53 #include "MainThreadUtils.h"
54 #include "nsLookAndFeel.h"
55 #include "nsUnicharUtils.h"
56 #include "nsWindowsHelpers.h"
57 #include "WinWindowOcclusionTracker.h"
59 #include <textstor.h>
60 #include "TSFTextStore.h"
62 #include <shlobj.h>
63 #include <shlwapi.h>
65 mozilla::LazyLogModule gWindowsLog("Widget");
67 #define LOG_E(...) MOZ_LOG(gWindowsLog, LogLevel::Error, (__VA_ARGS__))
68 #define LOG_D(...) MOZ_LOG(gWindowsLog, LogLevel::Debug, (__VA_ARGS__))
70 using namespace mozilla::gfx;
72 namespace mozilla {
73 namespace widget {
75 #ifdef MOZ_PLACES
76 NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
77 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
78 #endif
79 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
80 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
82 const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
83 const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
85 struct CoTaskMemFreePolicy {
86 void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
89 SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
90 EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
91 GetSystemMetricsForDpiProc WinUtils::sGetSystemMetricsForDpi = NULL;
92 bool WinUtils::sHasPackageIdentity = false;
94 using GetDpiForWindowProc = UINT(WINAPI*)(HWND);
95 static GetDpiForWindowProc sGetDpiForWindow = NULL;
97 /* static */
98 void WinUtils::Initialize() {
99 // Dpi-Awareness is not supported with Win32k Lockdown enabled, so we don't
100 // initialize DPI-related members and assert later that nothing accidently
101 // uses these static members
102 if (IsWin10OrLater() && !IsWin32kLockedDown()) {
103 HMODULE user32Dll = ::GetModuleHandleW(L"user32");
104 if (user32Dll) {
105 auto getThreadDpiAwarenessContext =
106 (decltype(GetThreadDpiAwarenessContext)*)::GetProcAddress(
107 user32Dll, "GetThreadDpiAwarenessContext");
108 auto areDpiAwarenessContextsEqual =
109 (decltype(AreDpiAwarenessContextsEqual)*)::GetProcAddress(
110 user32Dll, "AreDpiAwarenessContextsEqual");
111 if (getThreadDpiAwarenessContext && areDpiAwarenessContextsEqual &&
112 areDpiAwarenessContextsEqual(
113 getThreadDpiAwarenessContext(),
114 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
115 // Only per-monitor v1 requires these workarounds.
116 sEnableNonClientDpiScaling =
117 (EnableNonClientDpiScalingProc)::GetProcAddress(
118 user32Dll, "EnableNonClientDpiScaling");
119 sSetThreadDpiAwarenessContext =
120 (SetThreadDpiAwarenessContextProc)::GetProcAddress(
121 user32Dll, "SetThreadDpiAwarenessContext");
124 sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)::GetProcAddress(
125 user32Dll, "GetSystemMetricsForDpi");
126 sGetDpiForWindow =
127 (GetDpiForWindowProc)::GetProcAddress(user32Dll, "GetDpiForWindow");
131 if (IsWin8OrLater()) {
132 sHasPackageIdentity = mozilla::HasPackageIdentity();
136 // static
137 LRESULT WINAPI WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg,
138 WPARAM wParam,
139 LPARAM lParam) {
140 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
142 // NOTE: this function was copied out into the body of the pre-XUL skeleton
143 // UI window proc (PreXULSkeletonUI.cpp). If this function changes at any
144 // point, we should probably factor this out and use it from both locations.
145 if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) {
146 sEnableNonClientDpiScaling(hWnd);
148 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
151 // static
152 void WinUtils::LogW(const wchar_t* fmt, ...) {
153 va_list args = nullptr;
154 if (!lstrlenW(fmt)) {
155 return;
157 va_start(args, fmt);
158 int buflen = _vscwprintf(fmt, args);
159 wchar_t* buffer = new wchar_t[buflen + 1];
160 if (!buffer) {
161 va_end(args);
162 return;
164 vswprintf(buffer, buflen, fmt, args);
165 va_end(args);
167 // MSVC, including remote debug sessions
168 OutputDebugStringW(buffer);
169 OutputDebugStringW(L"\n");
171 int len =
172 WideCharToMultiByte(CP_ACP, 0, buffer, -1, nullptr, 0, nullptr, nullptr);
173 if (len) {
174 char* utf8 = new char[len];
175 if (WideCharToMultiByte(CP_ACP, 0, buffer, -1, utf8, len, nullptr,
176 nullptr) > 0) {
177 // desktop console
178 printf("%s\n", utf8);
179 NS_ASSERTION(gWindowsLog,
180 "Called WinUtils Log() but Widget "
181 "log module doesn't exist!");
182 MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", utf8));
184 delete[] utf8;
186 delete[] buffer;
189 // static
190 void WinUtils::Log(const char* fmt, ...) {
191 va_list args = nullptr;
192 if (!strlen(fmt)) {
193 return;
195 va_start(args, fmt);
196 int buflen = _vscprintf(fmt, args);
197 char* buffer = new char[buflen + 1];
198 if (!buffer) {
199 va_end(args);
200 return;
202 vsprintf(buffer, fmt, args);
203 va_end(args);
205 // MSVC, including remote debug sessions
206 OutputDebugStringA(buffer);
207 OutputDebugStringW(L"\n");
209 // desktop console
210 printf("%s\n", buffer);
212 NS_ASSERTION(gWindowsLog,
213 "Called WinUtils Log() but Widget "
214 "log module doesn't exist!");
215 MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", buffer));
216 delete[] buffer;
219 // static
220 float WinUtils::SystemDPI() {
221 // The result of GetDeviceCaps won't change dynamically, as it predates
222 // per-monitor DPI and support for on-the-fly resolution changes.
223 // Therefore, we only need to look it up once.
224 static float dpi = 0;
225 if (dpi <= 0) {
226 HDC screenDC = GetDC(nullptr);
227 dpi = GetDeviceCaps(screenDC, LOGPIXELSY);
228 ReleaseDC(nullptr, screenDC);
231 // Bug 1012487 - dpi can be 0 when the Screen DC is used off the
232 // main thread on windows. For now just assume a 100% DPI for this
233 // drawing call.
234 // XXX - fixme!
235 return dpi > 0 ? dpi : 96;
238 // static
239 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
241 #if WINVER < 0x603
242 typedef enum {
243 MDT_EFFECTIVE_DPI = 0,
244 MDT_ANGULAR_DPI = 1,
245 MDT_RAW_DPI = 2,
246 MDT_DEFAULT = MDT_EFFECTIVE_DPI
247 } MONITOR_DPI_TYPE;
249 typedef enum {
250 PROCESS_DPI_UNAWARE = 0,
251 PROCESS_SYSTEM_DPI_AWARE = 1,
252 PROCESS_PER_MONITOR_DPI_AWARE = 2
253 } PROCESS_DPI_AWARENESS;
254 #endif
256 typedef HRESULT(WINAPI* GETDPIFORMONITORPROC)(HMONITOR, MONITOR_DPI_TYPE, UINT*,
257 UINT*);
259 typedef HRESULT(WINAPI* GETPROCESSDPIAWARENESSPROC)(HANDLE,
260 PROCESS_DPI_AWARENESS*);
262 GETDPIFORMONITORPROC sGetDpiForMonitor;
263 GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness;
265 static bool SlowIsPerMonitorDPIAware() {
266 // Intentionally leak the handle.
267 HMODULE shcore = LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
268 if (shcore) {
269 sGetDpiForMonitor =
270 (GETDPIFORMONITORPROC)GetProcAddress(shcore, "GetDpiForMonitor");
271 sGetProcessDpiAwareness = (GETPROCESSDPIAWARENESSPROC)GetProcAddress(
272 shcore, "GetProcessDpiAwareness");
274 PROCESS_DPI_AWARENESS dpiAwareness;
275 return sGetDpiForMonitor && sGetProcessDpiAwareness &&
276 SUCCEEDED(
277 sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness)) &&
278 dpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE;
281 /* static */
282 bool WinUtils::IsPerMonitorDPIAware() {
283 static bool perMonitorDPIAware = SlowIsPerMonitorDPIAware();
284 return perMonitorDPIAware;
287 /* static */
288 float WinUtils::MonitorDPI(HMONITOR aMonitor) {
289 if (IsPerMonitorDPIAware()) {
290 UINT dpiX, dpiY = 96;
291 sGetDpiForMonitor(aMonitor ? aMonitor : GetPrimaryMonitor(),
292 MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
293 return dpiY;
296 // We're not per-monitor aware, use system DPI instead.
297 return SystemDPI();
300 /* static */
301 double WinUtils::LogToPhysFactor(HMONITOR aMonitor) {
302 return MonitorDPI(aMonitor) / 96.0;
305 /* static */
306 int32_t WinUtils::LogToPhys(HMONITOR aMonitor, double aValue) {
307 return int32_t(NS_round(aValue * LogToPhysFactor(aMonitor)));
310 /* static */
311 double WinUtils::LogToPhysFactor(HWND aWnd) {
312 // if there's an ancestor window, we want to share its DPI setting
313 HWND ancestor = ::GetAncestor(aWnd, GA_ROOTOWNER);
315 // The GetDpiForWindow api is not available everywhere where we run as
316 // per-monitor, but if it is available rely on it to tell us the scale
317 // factor of the window. See bug 1722085.
318 if (sGetDpiForWindow) {
319 UINT dpi = sGetDpiForWindow(ancestor ? ancestor : aWnd);
320 if (dpi > 0) {
321 return static_cast<double>(dpi) / 96.0;
324 return LogToPhysFactor(::MonitorFromWindow(ancestor ? ancestor : aWnd,
325 MONITOR_DEFAULTTOPRIMARY));
328 /* static */
329 HMONITOR
330 WinUtils::GetPrimaryMonitor() {
331 const POINT pt = {0, 0};
332 return ::MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
335 /* static */
336 HMONITOR
337 WinUtils::MonitorFromRect(const gfx::Rect& rect) {
338 // convert coordinates from desktop to device pixels for MonitorFromRect
339 double dpiScale =
340 IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor());
342 RECT globalWindowBounds = {NSToIntRound(dpiScale * rect.X()),
343 NSToIntRound(dpiScale * rect.Y()),
344 NSToIntRound(dpiScale * (rect.XMost())),
345 NSToIntRound(dpiScale * (rect.YMost()))};
347 return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST);
350 /* static */
351 bool WinUtils::HasSystemMetricsForDpi() {
352 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
353 return (sGetSystemMetricsForDpi != NULL);
356 /* static */
357 int WinUtils::GetSystemMetricsForDpi(int nIndex, UINT dpi) {
358 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
359 if (HasSystemMetricsForDpi()) {
360 return sGetSystemMetricsForDpi(nIndex, dpi);
361 } else {
362 double scale = IsPerMonitorDPIAware() ? dpi / SystemDPI() : 1.0;
363 return NSToIntRound(::GetSystemMetrics(nIndex) * scale);
367 /* static */
368 gfx::MarginDouble WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC aHdc) {
369 if (!aHdc) {
370 return gfx::MarginDouble();
373 int pixelsPerInchY = ::GetDeviceCaps(aHdc, LOGPIXELSY);
374 int marginTop = ::GetDeviceCaps(aHdc, PHYSICALOFFSETY);
375 int printableAreaHeight = ::GetDeviceCaps(aHdc, VERTRES);
376 int physicalHeight = ::GetDeviceCaps(aHdc, PHYSICALHEIGHT);
378 double marginTopInch = double(marginTop) / pixelsPerInchY;
380 double printableAreaHeightInch = double(printableAreaHeight) / pixelsPerInchY;
381 double physicalHeightInch = double(physicalHeight) / pixelsPerInchY;
382 double marginBottomInch =
383 physicalHeightInch - printableAreaHeightInch - marginTopInch;
385 int pixelsPerInchX = ::GetDeviceCaps(aHdc, LOGPIXELSX);
386 int marginLeft = ::GetDeviceCaps(aHdc, PHYSICALOFFSETX);
387 int printableAreaWidth = ::GetDeviceCaps(aHdc, HORZRES);
388 int physicalWidth = ::GetDeviceCaps(aHdc, PHYSICALWIDTH);
390 double marginLeftInch = double(marginLeft) / pixelsPerInchX;
392 double printableAreaWidthInch = double(printableAreaWidth) / pixelsPerInchX;
393 double physicalWidthInch = double(physicalWidth) / pixelsPerInchX;
394 double marginRightInch =
395 physicalWidthInch - printableAreaWidthInch - marginLeftInch;
397 return gfx::MarginDouble(marginTopInch, marginRightInch, marginBottomInch,
398 marginLeftInch);
401 #ifdef ACCESSIBILITY
402 /* static */
403 a11y::LocalAccessible* WinUtils::GetRootAccessibleForHWND(HWND aHwnd) {
404 nsWindow* window = GetNSWindowPtr(aHwnd);
405 if (!window) {
406 return nullptr;
409 return window->GetAccessible();
411 #endif // ACCESSIBILITY
413 /* static */
414 bool WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
415 UINT aLastMessage, UINT aOption) {
416 RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
417 if (msgPump) {
418 BOOL ret = FALSE;
419 HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
420 aOption, &ret);
421 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
422 return ret;
424 return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption);
427 /* static */
428 bool WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
429 UINT aLastMessage) {
430 RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
431 if (msgPump) {
432 BOOL ret = FALSE;
433 HRESULT hr =
434 msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, &ret);
435 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
436 return ret;
438 return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
441 #if defined(ACCESSIBILITY)
442 static DWORD GetWaitFlags() {
443 DWORD result = MWMO_INPUTAVAILABLE;
444 if (XRE_IsContentProcess()) {
445 result |= MWMO_ALERTABLE;
447 return result;
449 #endif
451 /* static */
452 void WinUtils::WaitForMessage(DWORD aTimeoutMs) {
453 #if defined(ACCESSIBILITY)
454 static const DWORD waitFlags = GetWaitFlags();
455 #else
456 const DWORD waitFlags = MWMO_INPUTAVAILABLE;
457 #endif
459 const DWORD waitStart = ::GetTickCount();
460 DWORD elapsed = 0;
461 while (true) {
462 if (aTimeoutMs != INFINITE) {
463 elapsed = ::GetTickCount() - waitStart;
465 if (elapsed >= aTimeoutMs) {
466 break;
468 DWORD result;
470 AUTO_PROFILER_THREAD_SLEEP;
471 result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
472 MOZ_QS_ALLEVENT, waitFlags);
474 NS_WARNING_ASSERTION(result != WAIT_FAILED, "Wait failed");
475 if (result == WAIT_TIMEOUT) {
476 break;
478 #if defined(ACCESSIBILITY)
479 if (result == WAIT_IO_COMPLETION) {
480 if (NS_IsMainThread()) {
481 // We executed an APC that would have woken up the hang monitor. Since
482 // there are no more APCs pending and we are now going to sleep again,
483 // we should notify the hang monitor.
484 mozilla::BackgroundHangMonitor().NotifyWait();
486 continue;
488 #endif // defined(ACCESSIBILITY)
490 // Sent messages (via SendMessage and friends) are processed differently
491 // than queued messages (via PostMessage); the destination window procedure
492 // of the sent message is called during (Get|Peek)Message. Since PeekMessage
493 // does not tell us whether it processed any sent messages, we need to query
494 // this ahead of time.
495 bool haveSentMessagesPending =
496 (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
498 MSG msg = {0};
499 if (haveSentMessagesPending ||
500 ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
501 break;
503 // The message is intended for another thread that has been synchronized
504 // with our input queue; yield to give other threads an opportunity to
505 // process the message. This should prevent busy waiting if resumed due
506 // to another thread's message.
507 ::SwitchToThread();
511 /* static */
512 bool WinUtils::GetRegistryKey(HKEY aRoot, char16ptr_t aKeyName,
513 char16ptr_t aValueName, wchar_t* aBuffer,
514 DWORD aBufferLength) {
515 MOZ_ASSERT(aKeyName, "The key name is NULL");
517 HKEY key;
518 LONG result =
519 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
520 if (result != ERROR_SUCCESS) {
521 result =
522 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
523 if (result != ERROR_SUCCESS) {
524 return false;
528 DWORD type;
529 result = ::RegQueryValueExW(key, aValueName, nullptr, &type, (BYTE*)aBuffer,
530 &aBufferLength);
531 ::RegCloseKey(key);
532 if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
533 return false;
535 if (aBuffer) {
536 aBuffer[aBufferLength / sizeof(*aBuffer) - 1] = 0;
538 return true;
541 /* static */
542 bool WinUtils::HasRegistryKey(HKEY aRoot, char16ptr_t aKeyName) {
543 MOZ_ASSERT(aRoot, "aRoot must not be NULL");
544 MOZ_ASSERT(aKeyName, "aKeyName must not be NULL");
545 HKEY key;
546 LONG result =
547 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
548 if (result != ERROR_SUCCESS) {
549 result =
550 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
551 if (result != ERROR_SUCCESS) {
552 return false;
555 ::RegCloseKey(key);
556 return true;
559 /* static */
560 HWND WinUtils::GetTopLevelHWND(HWND aWnd, bool aStopIfNotChild,
561 bool aStopIfNotPopup) {
562 HWND curWnd = aWnd;
563 HWND topWnd = nullptr;
565 while (curWnd) {
566 topWnd = curWnd;
568 if (aStopIfNotChild) {
569 DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE);
571 VERIFY_WINDOW_STYLE(style);
573 if (!(style & WS_CHILD)) // first top-level window
574 break;
577 HWND upWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
579 // GetParent will only return the owner if the passed in window
580 // has the WS_POPUP style.
581 if (!upWnd && !aStopIfNotPopup) {
582 upWnd = ::GetWindow(curWnd, GW_OWNER);
584 curWnd = upWnd;
587 return topWnd;
590 // Map from native window handles to nsWindow structures. Does not AddRef.
591 // Inherently unsafe to access outside the main thread.
592 static nsTHashMap<HWND, nsWindow*> sExtantNSWindows;
594 /* static */
595 void WinUtils::SetNSWindowPtr(HWND aWnd, nsWindow* aWindow) {
596 MOZ_ASSERT(NS_IsMainThread());
597 if (!aWindow) {
598 sExtantNSWindows.Remove(aWnd);
599 } else {
600 sExtantNSWindows.InsertOrUpdate(aWnd, aWindow);
604 /* static */
605 nsWindow* WinUtils::GetNSWindowPtr(HWND aWnd) {
606 MOZ_ASSERT(NS_IsMainThread());
607 return sExtantNSWindows.Get(aWnd); // or nullptr
610 static BOOL CALLBACK AddMonitor(HMONITOR, HDC, LPRECT, LPARAM aParam) {
611 (*(int32_t*)aParam)++;
612 return TRUE;
615 /* static */
616 int32_t WinUtils::GetMonitorCount() {
617 int32_t monitorCount = 0;
618 EnumDisplayMonitors(nullptr, nullptr, AddMonitor, (LPARAM)&monitorCount);
619 return monitorCount;
622 /* static */
623 bool WinUtils::IsOurProcessWindow(HWND aWnd) {
624 if (!aWnd) {
625 return false;
627 DWORD processId = 0;
628 ::GetWindowThreadProcessId(aWnd, &processId);
629 return (processId == ::GetCurrentProcessId());
632 /* static */
633 HWND WinUtils::FindOurProcessWindow(HWND aWnd) {
634 for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) {
635 if (IsOurProcessWindow(wnd)) {
636 return wnd;
639 return nullptr;
642 static bool IsPointInWindow(HWND aWnd, const POINT& aPointInScreen) {
643 RECT bounds;
644 if (!::GetWindowRect(aWnd, &bounds)) {
645 return false;
648 return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right &&
649 aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom);
653 * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
654 * forground in this context) of aWnd.
657 static HWND FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen) {
658 if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) {
659 return nullptr;
662 HWND childWnd = ::GetTopWindow(aWnd);
663 while (childWnd) {
664 HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen);
665 if (topmostWnd) {
666 return topmostWnd;
668 childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
671 return aWnd;
674 struct FindOurWindowAtPointInfo {
675 POINT mInPointInScreen;
676 HWND mOutWnd;
679 static BOOL CALLBACK FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM) {
680 if (!WinUtils::IsOurProcessWindow(aWnd)) {
681 // This isn't one of our top-level windows; continue enumerating.
682 return TRUE;
685 // Get the top-most child window under the point. If there's no child
686 // window, and the point is within the top-level window, then the top-level
687 // window will be returned. (This is the usual case. A child window
688 // would be returned for plugins.)
689 FindOurWindowAtPointInfo* info =
690 reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
691 HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen);
692 if (!childWnd) {
693 // This window doesn't contain the point; continue enumerating.
694 return TRUE;
697 // Return the HWND and stop enumerating.
698 info->mOutWnd = childWnd;
699 return FALSE;
702 /* static */
703 HWND WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen) {
704 FindOurWindowAtPointInfo info;
705 info.mInPointInScreen = aPointInScreen;
706 info.mOutWnd = nullptr;
708 // This will enumerate all top-level windows in order from top to bottom.
709 EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
710 return info.mOutWnd;
713 /* static */
714 UINT WinUtils::GetInternalMessage(UINT aNativeMessage) {
715 switch (aNativeMessage) {
716 case WM_MOUSEWHEEL:
717 return MOZ_WM_MOUSEVWHEEL;
718 case WM_MOUSEHWHEEL:
719 return MOZ_WM_MOUSEHWHEEL;
720 case WM_VSCROLL:
721 return MOZ_WM_VSCROLL;
722 case WM_HSCROLL:
723 return MOZ_WM_HSCROLL;
724 default:
725 return aNativeMessage;
729 /* static */
730 UINT WinUtils::GetNativeMessage(UINT aInternalMessage) {
731 switch (aInternalMessage) {
732 case MOZ_WM_MOUSEVWHEEL:
733 return WM_MOUSEWHEEL;
734 case MOZ_WM_MOUSEHWHEEL:
735 return WM_MOUSEHWHEEL;
736 case MOZ_WM_VSCROLL:
737 return WM_VSCROLL;
738 case MOZ_WM_HSCROLL:
739 return WM_HSCROLL;
740 default:
741 return aInternalMessage;
745 /* static */
746 uint16_t WinUtils::GetMouseInputSource() {
747 int32_t inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE;
748 LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
749 if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
750 inputSource = (lParamExtraInfo & TABLET_INK_TOUCH)
751 ? dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
752 : dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
754 return static_cast<uint16_t>(inputSource);
757 /* static */
758 uint16_t WinUtils::GetMousePointerID() {
759 LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
760 return lParamExtraInfo & TABLET_INK_ID_MASK;
763 /* static */
764 bool WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage) {
765 const uint32_t MOZ_T_I_SIGNATURE = TABLET_INK_TOUCH | TABLET_INK_SIGNATURE;
766 const uint32_t MOZ_T_I_CHECK_TCH = TABLET_INK_TOUCH | TABLET_INK_CHECK;
767 return ((aEventMessage == eMouseMove || aEventMessage == eMouseDown ||
768 aEventMessage == eMouseUp || aEventMessage == eMouseAuxClick ||
769 aEventMessage == eMouseDoubleClick) &&
770 (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE) == MOZ_T_I_CHECK_TCH);
773 /* static */
774 MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) {
775 MSG msg;
776 msg.message = aMessage;
777 msg.wParam = wParam;
778 msg.lParam = lParam;
779 msg.hwnd = aWnd;
780 return msg;
783 #ifdef MOZ_PLACES
784 /************************************************************************
785 * Constructs as AsyncFaviconDataReady Object
786 * @param aIOThread : the thread which performs the action
787 * @param aURLShortcut : Differentiates between (false)Jumplistcache and
788 * (true)Shortcutcache
789 * @param aRunnable : Executed in the aIOThread when the favicon cache is
790 * avaiable
791 ************************************************************************/
793 AsyncFaviconDataReady::AsyncFaviconDataReady(
794 nsIURI* aNewURI, RefPtr<LazyIdleThread>& aIOThread, const bool aURLShortcut,
795 already_AddRefed<nsIRunnable> aRunnable)
796 : mNewURI(aNewURI),
797 mIOThread(aIOThread),
798 mRunnable(aRunnable),
799 mURLShortcut(aURLShortcut) {}
801 NS_IMETHODIMP
802 myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
803 nsIRequest* request, nsresult status,
804 nsIFile* result) {
805 return NS_OK;
808 nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
809 if (!mURLShortcut) {
810 return NS_OK;
813 nsCOMPtr<nsIFile> icoFile;
814 nsresult rv =
815 FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
816 NS_ENSURE_SUCCESS(rv, rv);
818 nsCOMPtr<nsIURI> mozIconURI;
819 rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
820 if (NS_FAILED(rv)) {
821 return rv;
824 nsCOMPtr<nsIChannel> channel;
825 rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI,
826 nsContentUtils::GetSystemPrincipal(),
827 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
828 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
830 NS_ENSURE_SUCCESS(rv, rv);
832 nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
833 nsCOMPtr<nsIStreamListener> listener;
834 rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
835 NS_ENSURE_SUCCESS(rv, rv);
837 return channel->AsyncOpen(listener);
840 NS_IMETHODIMP
841 AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
842 const uint8_t* aData,
843 const nsACString& aMimeType,
844 uint16_t aWidth) {
845 if (!aDataLen || !aData) {
846 if (mURLShortcut) {
847 OnFaviconDataNotAvailable();
850 return NS_OK;
853 nsCOMPtr<nsIFile> icoFile;
854 nsresult rv =
855 FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
856 NS_ENSURE_SUCCESS(rv, rv);
858 nsAutoString path;
859 rv = icoFile->GetPath(path);
860 NS_ENSURE_SUCCESS(rv, rv);
862 // Decode the image from the format it was returned to us in (probably PNG)
863 nsCOMPtr<imgIContainer> container;
864 nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
865 rv = imgtool->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData),
866 aDataLen, aMimeType,
867 getter_AddRefs(container));
868 NS_ENSURE_SUCCESS(rv, rv);
870 RefPtr<SourceSurface> surface = container->GetFrame(
871 imgIContainer::FRAME_FIRST,
872 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
873 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
875 RefPtr<DataSourceSurface> dataSurface;
876 IntSize size;
878 if (mURLShortcut &&
879 (surface->GetSize().width < 48 || surface->GetSize().height < 48)) {
880 // Create a 48x48 surface and paint the icon into the central rect.
881 size.width = std::max(surface->GetSize().width, 48);
882 size.height = std::max(surface->GetSize().height, 48);
883 dataSurface =
884 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
885 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
887 DataSourceSurface::MappedSurface map;
888 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
889 return NS_ERROR_FAILURE;
892 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
893 BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
894 dataSurface->GetFormat());
895 if (!dt) {
896 gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
897 "CreateDrawTargetForData";
898 return NS_ERROR_OUT_OF_MEMORY;
900 dt->FillRect(Rect(0, 0, size.width, size.height),
901 ColorPattern(ToDeviceColor(sRGBColor::OpaqueWhite())));
902 IntPoint point;
903 point.x = (size.width - surface->GetSize().width) / 2;
904 point.y = (size.height - surface->GetSize().height) / 2;
905 dt->DrawSurface(surface,
906 Rect(point.x, point.y, surface->GetSize().width,
907 surface->GetSize().height),
908 Rect(Point(0, 0), Size(surface->GetSize().width,
909 surface->GetSize().height)));
911 dataSurface->Unmap();
912 } else {
913 // By using the input image surface's size, we may end up encoding
914 // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
915 // Windows will resize appropriately for us. If we want to encode ourselves
916 // one day because we like our resizing better, we'd have to manually
917 // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
918 // SM_CYSMICON. We don't support resizing images asynchronously at the
919 // moment anyway so getting the DPI aware icon size won't help.
920 size.width = surface->GetSize().width;
921 size.height = surface->GetSize().height;
922 dataSurface = surface->GetDataSurface();
923 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
926 // Allocate a new buffer that we own and can use out of line in
927 // another thread.
928 UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface);
929 if (!data) {
930 return NS_ERROR_OUT_OF_MEMORY;
932 int32_t stride = 4 * size.width;
934 // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
935 nsCOMPtr<nsIRunnable> event =
936 new AsyncEncodeAndWriteIcon(path, std::move(data), stride, size.width,
937 size.height, mRunnable.forget());
938 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
940 return NS_OK;
942 #endif
944 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
945 // in
946 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
947 const nsAString& aIconPath, UniquePtr<uint8_t[]> aBuffer, uint32_t aStride,
948 uint32_t aWidth, uint32_t aHeight, already_AddRefed<nsIRunnable> aRunnable)
949 : mIconPath(aIconPath),
950 mBuffer(std::move(aBuffer)),
951 mRunnable(aRunnable),
952 mStride(aStride),
953 mWidth(aWidth),
954 mHeight(aHeight) {}
956 NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
957 MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");
959 // Note that since we're off the main thread we can't use
960 // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
961 RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
962 mBuffer.get(), mStride, IntSize(mWidth, mHeight),
963 SurfaceFormat::B8G8R8A8);
965 FILE* file = _wfopen(mIconPath.get(), L"wb");
966 if (!file) {
967 // Maybe the directory doesn't exist; try creating it, then fopen again.
968 nsresult rv = NS_ERROR_FAILURE;
969 nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
970 if (comFile) {
971 rv = comFile->InitWithPath(mIconPath);
972 if (NS_SUCCEEDED(rv)) {
973 nsCOMPtr<nsIFile> dirPath;
974 comFile->GetParent(getter_AddRefs(dirPath));
975 if (dirPath) {
976 rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
977 if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
978 file = _wfopen(mIconPath.get(), L"wb");
979 if (!file) {
980 rv = NS_ERROR_FAILURE;
986 if (!file) {
987 return rv;
990 nsresult rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
991 gfxUtils::eBinaryEncode, file);
992 fclose(file);
993 NS_ENSURE_SUCCESS(rv, rv);
995 if (mRunnable) {
996 mRunnable->Run();
998 return rv;
1001 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {}
1003 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
1004 bool aIgnoreRecent)
1005 : mIgnoreRecent(aIgnoreRecent) {
1006 // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
1007 // threads, as it reads a pref, so cache its value here.
1008 mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600;
1010 // Prepare the profile directory cache on the main thread, to ensure we wont
1011 // do this on non-main threads.
1012 Unused << NS_GetSpecialDirectory("ProfLDS",
1013 getter_AddRefs(mJumpListCacheDir));
1016 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() {
1017 if (!mJumpListCacheDir) {
1018 return NS_ERROR_FAILURE;
1020 // Construct the path of our jump list cache
1021 nsresult rv = mJumpListCacheDir->AppendNative(
1022 nsDependentCString(FaviconHelper::kJumpListCacheDir));
1023 NS_ENSURE_SUCCESS(rv, rv);
1025 nsCOMPtr<nsIDirectoryEnumerator> entries;
1026 rv = mJumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
1027 NS_ENSURE_SUCCESS(rv, rv);
1029 // Loop through each directory entry and remove all ICO files found
1030 do {
1031 nsCOMPtr<nsIFile> currFile;
1032 if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
1033 break;
1035 nsAutoString path;
1036 if (NS_FAILED(currFile->GetPath(path))) continue;
1038 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
1039 // Check if the cached ICO file exists
1040 bool exists;
1041 if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
1043 if (mIgnoreRecent) {
1044 // Check to make sure the icon wasn't just recently created.
1045 // If it was created recently, don't delete it yet.
1046 int64_t fileModTime = 0;
1047 rv = currFile->GetLastModifiedTime(&fileModTime);
1048 fileModTime /= PR_MSEC_PER_SEC;
1049 // If the icon is older than the regeneration time (+ 10 min to be
1050 // safe), then it's old and we can get rid of it.
1051 // This code is only hit directly after a regeneration.
1052 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1053 if (NS_FAILED(rv) || (nowTime - fileModTime) < mIcoNoDeleteSeconds) {
1054 continue;
1058 // We found an ICO file that exists, so we should remove it
1059 currFile->Remove(false);
1061 } while (true);
1063 return NS_OK;
1066 AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() {}
1069 * (static) If the data is available, will return the path on disk where
1070 * the favicon for page aFaviconPageURI is stored. If the favicon does not
1071 * exist, or its cache is expired, this method will kick off an async request
1072 * for the icon so that next time the method is called it will be available.
1073 * @param aFaviconPageURI The URI of the page to obtain
1074 * @param aICOFilePath The path of the icon file
1075 * @param aIOThread The thread to perform the Fetch on
1076 * @param aURLShortcut to distinguish between jumplistcache(false) and
1077 * shortcutcache(true)
1078 * @param aRunnable Executed in the aIOThread when the favicon cache is
1079 * avaiable
1081 nsresult FaviconHelper::ObtainCachedIconFile(
1082 nsCOMPtr<nsIURI> aFaviconPageURI, nsString& aICOFilePath,
1083 RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut,
1084 already_AddRefed<nsIRunnable> aRunnable) {
1085 nsCOMPtr<nsIRunnable> runnable = aRunnable;
1086 // Obtain the ICO file path
1087 nsCOMPtr<nsIFile> icoFile;
1088 nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
1089 NS_ENSURE_SUCCESS(rv, rv);
1091 // Check if the cached ICO file already exists
1092 bool exists;
1093 rv = icoFile->Exists(&exists);
1094 NS_ENSURE_SUCCESS(rv, rv);
1096 if (exists) {
1097 // Obtain the file's last modification date in seconds
1098 int64_t fileModTime = 0;
1099 rv = icoFile->GetLastModifiedTime(&fileModTime);
1100 fileModTime /= PR_MSEC_PER_SEC;
1101 int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
1102 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1104 // If the last mod call failed or the icon is old then re-cache it
1105 // This check is in case the favicon of a page changes
1106 // the next time we try to build the jump list, the data will be available.
1107 if (NS_FAILED(rv) || (nowTime - fileModTime) > icoReCacheSecondsTimeout) {
1108 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1109 aURLShortcut, runnable.forget());
1110 return NS_ERROR_NOT_AVAILABLE;
1112 } else {
1113 // The file does not exist yet, obtain it async from the favicon service so
1114 // that the next time we try to build the jump list it'll be available.
1115 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1116 aURLShortcut, runnable.forget());
1117 return NS_ERROR_NOT_AVAILABLE;
1120 // The icoFile is filled with a path that exists, get its path
1121 rv = icoFile->GetPath(aICOFilePath);
1122 return rv;
1125 nsresult FaviconHelper::HashURI(nsCOMPtr<nsICryptoHash>& aCryptoHash,
1126 nsIURI* aUri, nsACString& aUriHash) {
1127 if (!aUri) return NS_ERROR_INVALID_ARG;
1129 nsAutoCString spec;
1130 nsresult rv = aUri->GetSpec(spec);
1131 NS_ENSURE_SUCCESS(rv, rv);
1133 if (!aCryptoHash) {
1134 aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1135 NS_ENSURE_SUCCESS(rv, rv);
1138 rv = aCryptoHash->Init(nsICryptoHash::MD5);
1139 NS_ENSURE_SUCCESS(rv, rv);
1140 rv = aCryptoHash->Update(
1141 reinterpret_cast<const uint8_t*>(spec.BeginReading()), spec.Length());
1142 NS_ENSURE_SUCCESS(rv, rv);
1143 rv = aCryptoHash->Finish(true, aUriHash);
1144 NS_ENSURE_SUCCESS(rv, rv);
1146 return NS_OK;
1149 // (static) Obtains the ICO file for the favicon at page aFaviconPageURI
1150 // If successful, the file path on disk is in the format:
1151 // <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
1152 nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
1153 nsCOMPtr<nsIFile>& aICOFile,
1154 bool aURLShortcut) {
1155 // Hash the input URI and replace any / with _
1156 nsAutoCString inputURIHash;
1157 nsCOMPtr<nsICryptoHash> cryptoHash;
1158 nsresult rv = HashURI(cryptoHash, aFaviconPageURI, inputURIHash);
1159 NS_ENSURE_SUCCESS(rv, rv);
1160 char* cur = inputURIHash.BeginWriting();
1161 char* end = inputURIHash.EndWriting();
1162 for (; cur < end; ++cur) {
1163 if ('/' == *cur) {
1164 *cur = '_';
1168 // Obtain the local profile directory and construct the output icon file path
1169 rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
1170 NS_ENSURE_SUCCESS(rv, rv);
1171 if (!aURLShortcut)
1172 rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
1173 else
1174 rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
1175 NS_ENSURE_SUCCESS(rv, rv);
1177 // Append the icon extension
1178 inputURIHash.AppendLiteral(".ico");
1179 rv = aICOFile->AppendNative(inputURIHash);
1181 return rv;
1184 // (static) Asynchronously creates a cached ICO file on disk for the favicon of
1185 // page aFaviconPageURI and stores it to disk at the path of aICOFile.
1186 nsresult FaviconHelper::CacheIconFileFromFaviconURIAsync(
1187 nsCOMPtr<nsIURI> aFaviconPageURI, nsCOMPtr<nsIFile> aICOFile,
1188 RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut,
1189 already_AddRefed<nsIRunnable> aRunnable) {
1190 nsCOMPtr<nsIRunnable> runnable = aRunnable;
1191 #ifdef MOZ_PLACES
1192 // Obtain the favicon service and get the favicon for the specified page
1193 nsCOMPtr<nsIFaviconService> favIconSvc(
1194 do_GetService("@mozilla.org/browser/favicon-service;1"));
1195 NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
1197 nsCOMPtr<nsIFaviconDataCallback> callback =
1198 new mozilla::widget::AsyncFaviconDataReady(
1199 aFaviconPageURI, aIOThread, aURLShortcut, runnable.forget());
1201 favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
1202 #endif
1203 return NS_OK;
1206 // Obtains the jump list 'ICO cache timeout in seconds' pref
1207 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
1208 // Only obtain the setting at most once from the pref service.
1209 // In the rare case that 2 threads call this at the same
1210 // time it is no harm and we will simply obtain the pref twice.
1211 // None of the taskbar list prefs are currently updated via a
1212 // pref observer so I think this should suffice.
1213 const int32_t kSecondsPerDay = 86400;
1214 static bool alreadyObtained = false;
1215 static int32_t icoReCacheSecondsTimeout = kSecondsPerDay;
1216 if (alreadyObtained) {
1217 return icoReCacheSecondsTimeout;
1220 // Obtain the pref
1221 const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1222 icoReCacheSecondsTimeout =
1223 Preferences::GetInt(PREF_ICOTIMEOUT, kSecondsPerDay);
1224 alreadyObtained = true;
1225 return icoReCacheSecondsTimeout;
1228 /* static */
1229 LayoutDeviceIntRegion WinUtils::ConvertHRGNToRegion(HRGN aRgn) {
1230 NS_ASSERTION(aRgn, "Don't pass NULL region here");
1232 LayoutDeviceIntRegion rgn;
1234 DWORD size = ::GetRegionData(aRgn, 0, nullptr);
1235 AutoTArray<uint8_t, 100> buffer;
1236 buffer.SetLength(size);
1238 RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements());
1239 if (!::GetRegionData(aRgn, size, data)) return rgn;
1241 if (data->rdh.nCount > MAX_RECTS_IN_REGION) {
1242 rgn = ToIntRect(data->rdh.rcBound);
1243 return rgn;
1246 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
1247 for (uint32_t i = 0; i < data->rdh.nCount; ++i) {
1248 RECT* r = rects + i;
1249 rgn.Or(rgn, ToIntRect(*r));
1252 return rgn;
1255 LayoutDeviceIntRect WinUtils::ToIntRect(const RECT& aRect) {
1256 return LayoutDeviceIntRect(aRect.left, aRect.top, aRect.right - aRect.left,
1257 aRect.bottom - aRect.top);
1260 /* static */
1261 bool WinUtils::IsIMEEnabled(const InputContext& aInputContext) {
1262 return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
1265 /* static */
1266 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState) {
1267 return aIMEState == IMEEnabled::Enabled;
1270 /* static */
1271 void WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray,
1272 uint32_t aModifiers, UINT aMessage) {
1273 MOZ_ASSERT(!(aModifiers & nsIWidget::ALTGRAPH) ||
1274 !(aModifiers & (nsIWidget::CTRL_L | nsIWidget::ALT_R)));
1275 if (aMessage == WM_KEYUP) {
1276 // If AltGr is released, ControlLeft key is released first, then,
1277 // AltRight key is released.
1278 if (aModifiers & nsIWidget::ALTGRAPH) {
1279 aArray->AppendElement(
1280 KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1281 aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1283 for (uint32_t i = ArrayLength(sModifierKeyMap); i; --i) {
1284 const uint32_t* map = sModifierKeyMap[i - 1];
1285 if (aModifiers & map[0]) {
1286 aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1289 } else {
1290 for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) {
1291 const uint32_t* map = sModifierKeyMap[i];
1292 if (aModifiers & map[0]) {
1293 aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1296 // If AltGr is pressed, ControlLeft key is pressed first, then,
1297 // AltRight key is pressed.
1298 if (aModifiers & nsIWidget::ALTGRAPH) {
1299 aArray->AppendElement(
1300 KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1301 aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1306 /* static */
1307 nsresult WinUtils::WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
1308 RefPtr<SourceSurface> surface = aImage->GetFrame(
1309 imgIContainer::FRAME_FIRST,
1310 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
1311 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
1313 return WriteBitmap(aFile, surface);
1316 /* static */
1317 nsresult WinUtils::WriteBitmap(nsIFile* aFile, SourceSurface* surface) {
1318 nsresult rv;
1320 // For either of the following formats we want to set the biBitCount member
1321 // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
1322 // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
1323 // for the BI_RGB value we use for the biCompression member.
1324 MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
1325 surface->GetFormat() == SurfaceFormat::B8G8R8X8);
1327 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
1328 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
1330 int32_t width = dataSurface->GetSize().width;
1331 int32_t height = dataSurface->GetSize().height;
1332 int32_t bytesPerPixel = 4 * sizeof(uint8_t);
1333 uint32_t bytesPerRow = bytesPerPixel * width;
1334 bool hasAlpha = surface->GetFormat() == SurfaceFormat::B8G8R8A8;
1336 // initialize these bitmap structs which we will later
1337 // serialize directly to the head of the bitmap file
1338 BITMAPV4HEADER bmi;
1339 memset(&bmi, 0, sizeof(BITMAPV4HEADER));
1340 bmi.bV4Size = sizeof(BITMAPV4HEADER);
1341 bmi.bV4Width = width;
1342 bmi.bV4Height = height;
1343 bmi.bV4Planes = 1;
1344 bmi.bV4BitCount = (WORD)bytesPerPixel * 8;
1345 bmi.bV4V4Compression = hasAlpha ? BI_BITFIELDS : BI_RGB;
1346 bmi.bV4SizeImage = bytesPerRow * height;
1347 bmi.bV4CSType = LCS_sRGB;
1348 if (hasAlpha) {
1349 bmi.bV4RedMask = 0x00FF0000;
1350 bmi.bV4GreenMask = 0x0000FF00;
1351 bmi.bV4BlueMask = 0x000000FF;
1352 bmi.bV4AlphaMask = 0xFF000000;
1355 BITMAPFILEHEADER bf;
1356 DWORD colormask[3];
1357 bf.bfType = 0x4D42; // 'BM'
1358 bf.bfReserved1 = 0;
1359 bf.bfReserved2 = 0;
1360 bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) +
1361 (hasAlpha ? sizeof(colormask) : 0);
1362 bf.bfSize = bf.bfOffBits + bmi.bV4SizeImage;
1364 // get a file output stream
1365 nsCOMPtr<nsIOutputStream> stream;
1366 rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
1367 NS_ENSURE_SUCCESS(rv, rv);
1369 DataSourceSurface::MappedSurface map;
1370 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
1371 return NS_ERROR_FAILURE;
1374 // write the bitmap headers and rgb pixel data to the file
1375 rv = NS_ERROR_FAILURE;
1376 if (stream) {
1377 uint32_t written;
1378 stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
1379 if (written == sizeof(BITMAPFILEHEADER)) {
1380 stream->Write((const char*)&bmi, sizeof(BITMAPV4HEADER), &written);
1381 if (written == sizeof(BITMAPV4HEADER)) {
1382 if (hasAlpha) {
1383 // color mask
1384 colormask[0] = 0x00FF0000;
1385 colormask[1] = 0x0000FF00;
1386 colormask[2] = 0x000000FF;
1388 stream->Write((const char*)colormask, sizeof(colormask), &written);
1390 if (!hasAlpha || written == sizeof(colormask)) {
1391 // write out the image data backwards because the desktop won't
1392 // show bitmaps with negative heights for top-to-bottom
1393 uint32_t i = map.mStride * height;
1394 do {
1395 i -= map.mStride;
1396 stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
1397 if (written == bytesPerRow) {
1398 rv = NS_OK;
1399 } else {
1400 rv = NS_ERROR_FAILURE;
1401 break;
1403 } while (i != 0);
1408 stream->Close();
1411 dataSurface->Unmap();
1413 return rv;
1416 // This is in use here and in dom/events/TouchEvent.cpp
1417 /* static */
1418 uint32_t WinUtils::IsTouchDeviceSupportPresent() {
1419 int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
1420 int32_t touchFlags = NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH;
1421 if (StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled()) {
1422 touchFlags |= NID_EXTERNAL_PEN | NID_INTEGRATED_PEN;
1424 return (touchCapabilities & NID_READY) && (touchCapabilities & touchFlags);
1427 /* static */
1428 uint32_t WinUtils::GetMaxTouchPoints() {
1429 if (IsTouchDeviceSupportPresent()) {
1430 return GetSystemMetrics(SM_MAXIMUMTOUCHES);
1432 return 0;
1435 /* static */
1436 POWER_PLATFORM_ROLE
1437 WinUtils::GetPowerPlatformRole() {
1438 typedef POWER_PLATFORM_ROLE(WINAPI *
1439 PowerDeterminePlatformRoleEx)(ULONG Version);
1440 static PowerDeterminePlatformRoleEx power_determine_platform_role =
1441 reinterpret_cast<PowerDeterminePlatformRoleEx>(::GetProcAddress(
1442 ::LoadLibraryW(L"PowrProf.dll"), "PowerDeterminePlatformRoleEx"));
1444 POWER_PLATFORM_ROLE powerPlatformRole = PlatformRoleUnspecified;
1445 if (!power_determine_platform_role) {
1446 return powerPlatformRole;
1449 return power_determine_platform_role(POWER_PLATFORM_ROLE_V2);
1452 // static
1453 bool WinUtils::GetAutoRotationState(AR_STATE* aRotationState) {
1454 typedef BOOL(WINAPI * GetAutoRotationStateFunc)(PAR_STATE pState);
1455 static GetAutoRotationStateFunc get_auto_rotation_state_func =
1456 reinterpret_cast<GetAutoRotationStateFunc>(::GetProcAddress(
1457 GetModuleHandleW(L"user32.dll"), "GetAutoRotationState"));
1458 if (get_auto_rotation_state_func) {
1459 ZeroMemory(aRotationState, sizeof(AR_STATE));
1460 return get_auto_rotation_state_func(aRotationState);
1462 return false;
1465 static bool IsTabletDevice() {
1466 // Guarantees that:
1467 // - The device has a touch screen.
1468 // - It is used as a tablet which means that it has no keyboard connected.
1469 // On Windows 10 it means that it is verifying with ConvertibleSlateMode.
1471 if (!IsWin8OrLater()) {
1472 return false;
1475 if (WindowsUIUtils::GetInTabletMode()) {
1476 return true;
1479 if (!GetSystemMetrics(SM_MAXIMUMTOUCHES)) {
1480 return false;
1483 // If the device is docked, the user is treating the device as a PC.
1484 if (GetSystemMetrics(SM_SYSTEMDOCKED)) {
1485 return false;
1488 // If the device is not supporting rotation, it's unlikely to be a tablet,
1489 // a convertible or a detachable. See:
1490 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
1491 AR_STATE rotation_state;
1492 if (WinUtils::GetAutoRotationState(&rotation_state) &&
1493 (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR))) {
1494 return false;
1497 // PlatformRoleSlate was added in Windows 8+.
1498 POWER_PLATFORM_ROLE role = WinUtils::GetPowerPlatformRole();
1499 if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
1500 return !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
1502 return false;
1505 static bool IsMousePresent() {
1506 if (!::GetSystemMetrics(SM_MOUSEPRESENT)) {
1507 return false;
1510 DWORD count = InputDeviceUtils::CountMouseDevices();
1511 if (!count) {
1512 return false;
1515 // If there is a mouse device and if this machine is a tablet or has a
1516 // digitizer, that's counted as the mouse device.
1517 // FIXME: Bug 1495938: We should drop this heuristic way once we find out a
1518 // reliable way to tell there is no mouse or not.
1519 if (count == 1 &&
1520 (WinUtils::IsTouchDeviceSupportPresent() || IsTabletDevice())) {
1521 return false;
1524 return true;
1527 /* static */
1528 PointerCapabilities WinUtils::GetPrimaryPointerCapabilities() {
1529 if (IsTabletDevice()) {
1530 return PointerCapabilities::Coarse;
1533 if (IsMousePresent()) {
1534 return PointerCapabilities::Fine | PointerCapabilities::Hover;
1537 if (IsTouchDeviceSupportPresent()) {
1538 return PointerCapabilities::Coarse;
1541 return PointerCapabilities::None;
1544 /* static */
1545 PointerCapabilities WinUtils::GetAllPointerCapabilities() {
1546 PointerCapabilities result = PointerCapabilities::None;
1548 if (IsTabletDevice() || IsTouchDeviceSupportPresent()) {
1549 result |= PointerCapabilities::Coarse;
1552 if (IsMousePresent()) {
1553 result |= PointerCapabilities::Fine | PointerCapabilities::Hover;
1556 return result;
1559 /* static */
1560 bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath) {
1561 LOG_D("ResolveJunctionPointsAndSymLinks: Resolving path: %S", aPath.c_str());
1563 wchar_t path[MAX_PATH] = {0};
1565 nsAutoHandle handle(::CreateFileW(
1566 aPath.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1567 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
1569 if (handle == INVALID_HANDLE_VALUE) {
1570 LOG_E("Failed to open file handle to resolve path. GetLastError=%lu",
1571 GetLastError());
1572 return false;
1575 DWORD pathLen = GetFinalPathNameByHandleW(
1576 handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1577 if (pathLen == 0 || pathLen >= MAX_PATH) {
1578 LOG_E("GetFinalPathNameByHandleW failed. GetLastError=%lu", GetLastError());
1579 return false;
1581 aPath = path;
1583 // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
1584 // but that confuses some APIs so strip it off. It will also put
1585 // '\\?\UNC\' in front of network paths, we convert that to '\\'.
1586 if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
1587 aPath.erase(2, 6);
1588 } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
1589 aPath.erase(0, 4);
1592 LOG_D("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
1593 aPath.c_str());
1594 return true;
1597 /* static */
1598 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile* aPath) {
1599 MOZ_ASSERT(aPath);
1601 nsAutoString filePath;
1602 nsresult rv = aPath->GetPath(filePath);
1603 if (NS_WARN_IF(NS_FAILED(rv))) {
1604 return false;
1607 std::wstring resolvedPath(filePath.get());
1608 if (!ResolveJunctionPointsAndSymLinks(resolvedPath)) {
1609 return false;
1612 rv = aPath->InitWithPath(nsDependentString(resolvedPath.c_str()));
1613 if (NS_WARN_IF(NS_FAILED(rv))) {
1614 return false;
1617 return true;
1620 /* static */
1621 bool WinUtils::RunningFromANetworkDrive() {
1622 wchar_t exePath[MAX_PATH];
1623 if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) {
1624 return false;
1627 std::wstring exeString(exePath);
1628 if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString)) {
1629 return false;
1632 wchar_t volPath[MAX_PATH];
1633 if (!::GetVolumePathNameW(exeString.c_str(), volPath, MAX_PATH)) {
1634 return false;
1637 return (::GetDriveTypeW(volPath) == DRIVE_REMOTE);
1640 /* static */
1641 bool WinUtils::CanonicalizePath(nsAString& aPath) {
1642 wchar_t tempPath[MAX_PATH + 1];
1643 if (!PathCanonicalizeW(tempPath,
1644 (char16ptr_t)PromiseFlatString(aPath).get())) {
1645 return false;
1647 aPath = tempPath;
1648 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1649 return true;
1652 /* static */
1653 bool WinUtils::MakeLongPath(nsAString& aPath) {
1654 wchar_t tempPath[MAX_PATH + 1];
1655 DWORD longResult =
1656 GetLongPathNameW((char16ptr_t)PromiseFlatString(aPath).get(), tempPath,
1657 ArrayLength(tempPath));
1658 if (longResult > ArrayLength(tempPath)) {
1659 // Our buffer is too short, and we're guaranteeing <= MAX_PATH results.
1660 return false;
1661 } else if (longResult) {
1662 // Success.
1663 aPath = tempPath;
1664 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1666 // GetLongPathNameW returns 0 if the path is not found or is not rooted,
1667 // but we shouldn't consider that a failure condition.
1668 return true;
1671 /* static */
1672 bool WinUtils::UnexpandEnvVars(nsAString& aPath) {
1673 wchar_t tempPath[MAX_PATH + 1];
1674 // PathUnExpandEnvStringsW returns false if it doesn't make any
1675 // substitutions. Silently continue using the unaltered path.
1676 if (PathUnExpandEnvStringsW((char16ptr_t)PromiseFlatString(aPath).get(),
1677 tempPath, ArrayLength(tempPath))) {
1678 aPath = tempPath;
1679 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1681 return true;
1684 /* static */
1685 WinUtils::WhitelistVec WinUtils::BuildWhitelist() {
1686 WhitelistVec result;
1688 Unused << result.emplaceBack(
1689 std::make_pair(nsString(u"%ProgramFiles%"_ns), nsDependentString()));
1691 // When no substitution is required, set the void flag
1692 result.back().second.SetIsVoid(true);
1694 Unused << result.emplaceBack(
1695 std::make_pair(nsString(u"%SystemRoot%"_ns), nsDependentString()));
1696 result.back().second.SetIsVoid(true);
1698 wchar_t tmpPath[MAX_PATH + 1] = {};
1699 if (GetTempPath(MAX_PATH, tmpPath)) {
1700 // GetTempPath's result always ends with a backslash, which we don't want
1701 uint32_t tmpPathLen = wcslen(tmpPath);
1702 if (tmpPathLen) {
1703 tmpPath[tmpPathLen - 1] = 0;
1706 nsAutoString cleanTmpPath(tmpPath);
1707 if (UnexpandEnvVars(cleanTmpPath)) {
1708 constexpr auto tempVar = u"%TEMP%"_ns;
1709 Unused << result.emplaceBack(std::make_pair(
1710 nsString(cleanTmpPath), nsDependentString(tempVar, 0)));
1714 // If we add more items to the whitelist, ensure we still don't invoke an
1715 // unnecessary heap allocation.
1716 MOZ_ASSERT(result.length() <= kMaxWhitelistedItems);
1718 return result;
1722 * This function provides an array of (system path, substitution) pairs that are
1723 * considered to be acceptable with respect to privacy, for the purposes of
1724 * submitting within telemetry or crash reports.
1726 * The substitution string's void flag may be set. If it is, no subsitution is
1727 * necessary. Otherwise, the consumer should replace the system path with the
1728 * substitution.
1730 * @see PreparePathForTelemetry for an example of its usage.
1732 /* static */
1733 const WinUtils::WhitelistVec& WinUtils::GetWhitelistedPaths() {
1734 static WhitelistVec sWhitelist([]() -> WhitelistVec {
1735 auto setClearFn = [ptr = &sWhitelist]() -> void {
1736 RunOnShutdown([ptr]() -> void { ptr->clear(); },
1737 ShutdownPhase::XPCOMShutdownFinal);
1740 if (NS_IsMainThread()) {
1741 setClearFn();
1742 } else {
1743 SchedulerGroup::Dispatch(
1744 TaskCategory::Other,
1745 NS_NewRunnableFunction("WinUtils::GetWhitelistedPaths",
1746 std::move(setClearFn)));
1749 return BuildWhitelist();
1750 }());
1751 return sWhitelist;
1755 * This function is located here (as opposed to nsSystemInfo or elsewhere)
1756 * because we need to gather this information as early as possible during
1757 * startup.
1759 /* static */
1760 bool WinUtils::GetAppInitDLLs(nsAString& aOutput) {
1761 aOutput.Truncate();
1762 HKEY hkey = NULL;
1763 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1764 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
1765 0, KEY_QUERY_VALUE, &hkey)) {
1766 return false;
1768 nsAutoRegKey key(hkey);
1769 LONG status;
1770 const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
1771 DWORD loadAppInitDLLs = 0;
1772 DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
1773 status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr, nullptr,
1774 (LPBYTE)&loadAppInitDLLs, &loadAppInitDLLsLen);
1775 if (status != ERROR_SUCCESS) {
1776 return false;
1778 if (!loadAppInitDLLs) {
1779 // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
1780 // In this case we'll return true along with an empty output string.
1781 return true;
1783 DWORD numBytes = 0;
1784 const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
1785 // Query for required buffer size
1786 status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr,
1787 &numBytes);
1788 if (status != ERROR_SUCCESS) {
1789 return false;
1791 // Allocate the buffer and query for the actual data
1792 mozilla::UniquePtr<wchar_t[]> data =
1793 mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
1794 status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr,
1795 (LPBYTE)data.get(), &numBytes);
1796 if (status != ERROR_SUCCESS) {
1797 return false;
1799 // For each token, split up the filename components and then check the
1800 // name of the file.
1801 const wchar_t kDelimiters[] = L", ";
1802 wchar_t* tokenContext = nullptr;
1803 wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
1804 while (token) {
1805 nsAutoString cleanPath(token);
1806 // Since these paths are short paths originating from the registry, we need
1807 // to canonicalize them, lengthen them, and sanitize them before we can
1808 // check them against the whitelist
1809 if (PreparePathForTelemetry(cleanPath)) {
1810 if (!aOutput.IsEmpty()) {
1811 aOutput += L";";
1813 aOutput += cleanPath;
1815 token = wcstok_s(nullptr, kDelimiters, &tokenContext);
1817 return true;
1820 /* static */
1821 bool WinUtils::PreparePathForTelemetry(nsAString& aPath,
1822 PathTransformFlags aFlags) {
1823 if (aFlags & PathTransformFlags::Canonicalize) {
1824 if (!CanonicalizePath(aPath)) {
1825 return false;
1828 if (aFlags & PathTransformFlags::Lengthen) {
1829 if (!MakeLongPath(aPath)) {
1830 return false;
1833 if (aFlags & PathTransformFlags::UnexpandEnvVars) {
1834 if (!UnexpandEnvVars(aPath)) {
1835 return false;
1839 const WhitelistVec& whitelistedPaths = GetWhitelistedPaths();
1841 for (uint32_t i = 0; i < whitelistedPaths.length(); ++i) {
1842 const nsString& testPath = whitelistedPaths[i].first;
1843 const nsDependentString& substitution = whitelistedPaths[i].second;
1844 if (StringBeginsWith(aPath, testPath, nsCaseInsensitiveStringComparator)) {
1845 if (!substitution.IsVoid()) {
1846 aPath.Replace(0, testPath.Length(), substitution);
1848 return true;
1852 // For non-whitelisted paths, we strip the path component and just leave
1853 // the filename. We can't use nsLocalFile to do this because these paths may
1854 // begin with environment variables, and nsLocalFile doesn't like
1855 // non-absolute paths.
1856 const nsString& flatPath = PromiseFlatString(aPath);
1857 LPCWSTR leafStart = ::PathFindFileNameW(flatPath.get());
1858 ptrdiff_t cutLen = leafStart - flatPath.get();
1859 if (cutLen) {
1860 aPath.Cut(0, cutLen);
1861 } else if (aFlags & PathTransformFlags::RequireFilePath) {
1862 return false;
1865 return true;
1868 nsString WinUtils::GetPackageFamilyName() {
1869 nsString rv;
1871 UniquePtr<wchar_t[]> packageIdentity = mozilla::GetPackageFamilyName();
1872 if (packageIdentity) {
1873 rv = packageIdentity.get();
1876 return rv;
1879 bool WinUtils::GetClassName(HWND aHwnd, nsAString& aClassName) {
1880 const int bufferLength = 256;
1881 aClassName.SetLength(bufferLength);
1883 int length = ::GetClassNameW(aHwnd, (char16ptr_t)aClassName.BeginWriting(),
1884 bufferLength);
1885 if (length == 0) {
1886 return false;
1888 MOZ_RELEASE_ASSERT(length <= (bufferLength - 1));
1889 aClassName.Truncate(length);
1890 return true;
1893 static BOOL CALLBACK EnumUpdateWindowOcclusionProc(HWND aHwnd, LPARAM aLParam) {
1894 const bool* const enable = reinterpret_cast<bool*>(aLParam);
1895 nsWindow* window = WinUtils::GetNSWindowPtr(aHwnd);
1896 if (window) {
1897 window->MaybeEnableWindowOcclusion(*enable);
1899 return TRUE;
1902 void WinUtils::EnableWindowOcclusion(const bool aEnable) {
1903 if (aEnable) {
1904 WinWindowOcclusionTracker::Ensure();
1906 ::EnumWindows(EnumUpdateWindowOcclusionProc,
1907 reinterpret_cast<LPARAM>(&aEnable));
1910 bool WinUtils::GetTimezoneName(wchar_t* aBuffer) {
1911 DYNAMIC_TIME_ZONE_INFORMATION tzInfo;
1912 DWORD tzid = GetDynamicTimeZoneInformation(&tzInfo);
1914 if (tzid == TIME_ZONE_ID_INVALID) {
1915 return false;
1918 wcscpy_s(aBuffer, 128, tzInfo.TimeZoneKeyName);
1920 return true;
1923 // There are undocumented APIs to query/change the system DPI settings found by
1924 // https://github.com/lihas/ . We use those APIs only for testing purpose, i.e.
1925 // in mochitests or some such. To avoid exposing them in our official release
1926 // builds unexpectedly we restrict them only in debug builds.
1927 #ifdef DEBUG
1929 # define DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE (int)-4
1930 # define DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE (int)-3
1932 // Following two struts are copied from
1933 // https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.h
1936 * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
1937 * @brief used to fetch min, max, suggested, and currently applied DPI scaling
1938 * values. All values are relative to the recommended DPI scaling value Note
1939 * that DPI scaling is a property of the source, and not of target.
1941 struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET {
1942 DISPLAYCONFIG_DEVICE_INFO_HEADER header;
1944 * @brief min value of DPI scaling is always 100, minScaleRel gives no. of
1945 * steps down from recommended scaling eg. if minScaleRel is -3 => 100 is 3
1946 * steps down from recommended scaling => recommended scaling is 175%
1948 int32_t minScaleRel;
1951 * @brief currently applied DPI scaling value wrt the recommended value. eg.
1952 * if recommended value is 175%,
1953 * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1,
1954 * then current scale is 150%
1956 int32_t curScaleRel;
1959 * @brief maximum supported DPI scaling wrt recommended value
1961 int32_t maxScaleRel;
1965 * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
1966 * @brief set DPI scaling value of a source
1967 * Note that DPI scaling is a property of the source, and not of target.
1969 struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET {
1970 DISPLAYCONFIG_DEVICE_INFO_HEADER header;
1972 * @brief The value we want to set. The value should be relative to the
1973 * recommended DPI scaling value of source. eg. if scaleRel == 1, and
1974 * recommended value is 175% => we are trying to set 200% scaling for the
1975 * source
1977 int32_t scaleRel;
1980 static int32_t sCurRelativeScaleStep = std::numeric_limits<int32_t>::max();
1982 static LONG SetRelativeScaleStep(LUID aAdapterId, int32_t aRelativeScaleStep) {
1983 DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setDPIScale = {};
1984 setDPIScale.header.adapterId = aAdapterId;
1985 setDPIScale.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)
1986 DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE;
1987 setDPIScale.header.size = sizeof(setDPIScale);
1988 setDPIScale.scaleRel = aRelativeScaleStep;
1990 return DisplayConfigSetDeviceInfo(&setDPIScale.header);
1993 nsresult WinUtils::SetHiDPIMode(bool aHiDPI) {
1994 if (!IsWin10OrLater()) {
1995 return NS_ERROR_NOT_AVAILABLE;
1998 auto config = GetDisplayConfig();
1999 if (!config) {
2000 return NS_ERROR_NOT_AVAILABLE;
2003 if (config->mPaths.empty()) {
2004 return NS_ERROR_NOT_AVAILABLE;
2007 DISPLAYCONFIG_SOURCE_DPI_SCALE_GET dpiScale = {};
2008 dpiScale.header.adapterId = config->mPaths[0].targetInfo.adapterId;
2009 dpiScale.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)
2010 DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE;
2011 dpiScale.header.size = sizeof(dpiScale);
2012 LONG result = ::DisplayConfigGetDeviceInfo(&dpiScale.header);
2013 if (result != ERROR_SUCCESS) {
2014 return NS_ERROR_FAILURE;
2017 if (dpiScale.minScaleRel == dpiScale.maxScaleRel) {
2018 // We can't change the setting at all.
2019 return NS_ERROR_NOT_AVAILABLE;
2022 if (aHiDPI && dpiScale.curScaleRel == dpiScale.maxScaleRel) {
2023 // We've already at the maximum level.
2024 if (sCurRelativeScaleStep == std::numeric_limits<int32_t>::max()) {
2025 sCurRelativeScaleStep = dpiScale.curScaleRel;
2027 return NS_OK;
2030 if (!aHiDPI && dpiScale.curScaleRel == dpiScale.minScaleRel) {
2031 // We've already at the minimum level.
2032 if (sCurRelativeScaleStep == std::numeric_limits<int32_t>::max()) {
2033 sCurRelativeScaleStep = dpiScale.curScaleRel;
2035 return NS_OK;
2038 result = SetRelativeScaleStep(
2039 config->mPaths[0].targetInfo.adapterId,
2040 aHiDPI ? dpiScale.maxScaleRel : dpiScale.minScaleRel);
2041 if (result != ERROR_SUCCESS) {
2042 return NS_ERROR_FAILURE;
2045 if (sCurRelativeScaleStep == std::numeric_limits<int>::max()) {
2046 sCurRelativeScaleStep = dpiScale.curScaleRel;
2049 return NS_OK;
2052 nsresult WinUtils::RestoreHiDPIMode() {
2053 if (!IsWin10OrLater()) {
2054 return NS_ERROR_NOT_AVAILABLE;
2057 if (sCurRelativeScaleStep == std::numeric_limits<int>::max()) {
2058 // The DPI setting hasn't been changed.
2059 return NS_ERROR_UNEXPECTED;
2062 auto config = GetDisplayConfig();
2063 if (!config) {
2064 return NS_ERROR_NOT_AVAILABLE;
2067 if (config->mPaths.empty()) {
2068 return NS_ERROR_NOT_AVAILABLE;
2071 LONG result = SetRelativeScaleStep(config->mPaths[0].targetInfo.adapterId,
2072 sCurRelativeScaleStep);
2073 sCurRelativeScaleStep = std::numeric_limits<int32_t>::max();
2074 if (result != ERROR_SUCCESS) {
2075 return NS_ERROR_FAILURE;
2077 return NS_OK;
2079 #endif
2081 /* static */
2082 const char* WinUtils::WinEventToEventName(UINT msg) {
2083 const auto eventMsgInfo = mozilla::widget::gAllEvents.find(msg);
2084 return eventMsgInfo != mozilla::widget::gAllEvents.end()
2085 ? eventMsgInfo->second.mStr
2086 : nullptr;
2089 // Note to testers and/or test-authors: on Windows 10, and possibly on other
2090 // versions as well, supplying the `WS_EX_LAYOUTRTL` flag here has no effect
2091 // whatsoever on child common-dialogs **unless the system UI locale is also set
2092 // to an RTL language**.
2094 // If it is, the flag is still required; otherwise, the picker dialog will be
2095 // presented in English (or possibly some other LTR language) as a fallback.
2096 ScopedRtlShimWindow::ScopedRtlShimWindow(nsIWidget* aParent) : mWnd(nullptr) {
2097 NS_ENSURE_TRUE_VOID(aParent);
2099 // Headless windows don't have HWNDs, but also probably shouldn't be launching
2100 // print dialogs.
2101 HWND const hwnd = (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW);
2102 NS_ENSURE_TRUE_VOID(hwnd);
2104 nsWindow* const win = WinUtils::GetNSWindowPtr(hwnd);
2105 NS_ENSURE_TRUE_VOID(win);
2107 ATOM const wclass = ::GetClassWord(hwnd, GCW_ATOM);
2108 mWnd = ::CreateWindowExW(
2109 win->IsRTL() ? WS_EX_LAYOUTRTL : 0, (LPCWSTR)(uintptr_t)wclass, L"",
2110 WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
2111 hwnd, nullptr, nsToolkit::mDllInstance, nullptr);
2113 MOZ_ASSERT(mWnd);
2116 ScopedRtlShimWindow::~ScopedRtlShimWindow() {
2117 if (mWnd) {
2118 ::DestroyWindow(mWnd);
2122 } // namespace widget
2123 } // namespace mozilla