Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / windows / WinUtils.cpp
blob49cb929aa5f312878b387340ef0660b1b058ef4c
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 <psapi.h>
11 #include <winioctl.h>
13 #include "gfxPlatform.h"
14 #include "gfxUtils.h"
15 #include "nsWindow.h"
16 #include "nsWindowDefs.h"
17 #include "InputDeviceUtils.h"
18 #include "KeyboardLayout.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/BackgroundHangMonitor.h"
21 #include "mozilla/ClearOnShutdown.h"
22 #include "mozilla/StaticPrefs_widget.h"
23 #include "mozilla/dom/MouseEventBinding.h"
24 #include "mozilla/gfx/2D.h"
25 #include "mozilla/gfx/DataSurfaceHelpers.h"
26 #include "mozilla/gfx/DisplayConfigWindows.h"
27 #include "mozilla/gfx/Logging.h"
28 #include "mozilla/Preferences.h"
29 #include "mozilla/ProfilerThreadSleep.h"
30 #include "mozilla/RefPtr.h"
31 #include "mozilla/SchedulerGroup.h"
32 #include "mozilla/WinHeaderOnlyUtils.h"
33 #include "mozilla/Unused.h"
34 #include "nsIContentPolicy.h"
35 #include "WindowsUIUtils.h"
36 #include "nsContentUtils.h"
38 #include "mozilla/Logging.h"
40 #include "nsString.h"
41 #include "nsDirectoryServiceUtils.h"
42 #include "imgIContainer.h"
43 #include "imgITools.h"
44 #include "nsNetUtil.h"
45 #include "nsIOutputStream.h"
46 #include "nsNetCID.h"
47 #include "prtime.h"
48 #ifdef MOZ_PLACES
49 # include "nsIFaviconService.h"
50 #endif
51 #include "nsIDownloader.h"
52 #include "nsIChannel.h"
53 #include "nsIThread.h"
54 #include "MainThreadUtils.h"
55 #include "nsLookAndFeel.h"
56 #include "nsUnicharUtils.h"
57 #include "nsWindowsHelpers.h"
58 #include "WinWindowOcclusionTracker.h"
60 #include <textstor.h>
61 #include "TSFTextStore.h"
63 #include <shellscalingapi.h>
64 #include <shlobj.h>
65 #include <shlwapi.h>
67 mozilla::LazyLogModule gWindowsLog("Widget");
69 using namespace mozilla::gfx;
71 namespace mozilla {
72 namespace widget {
74 #ifdef MOZ_PLACES
75 NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
76 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
77 #endif
78 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
79 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
81 const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
82 const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
84 struct CoTaskMemFreePolicy {
85 void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
88 SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
89 EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
90 GetSystemMetricsForDpiProc WinUtils::sGetSystemMetricsForDpi = NULL;
91 bool WinUtils::sHasPackageIdentity = false;
93 using GetDpiForWindowProc = UINT(WINAPI*)(HWND);
94 static GetDpiForWindowProc sGetDpiForWindow = NULL;
96 /* static */
97 void WinUtils::Initialize() {
98 // Dpi-Awareness is not supported with Win32k Lockdown enabled, so we don't
99 // initialize DPI-related members and assert later that nothing accidently
100 // uses these static members
101 if (!IsWin32kLockedDown()) {
102 HMODULE user32Dll = ::GetModuleHandleW(L"user32");
103 if (user32Dll) {
104 auto getThreadDpiAwarenessContext =
105 (decltype(GetThreadDpiAwarenessContext)*)::GetProcAddress(
106 user32Dll, "GetThreadDpiAwarenessContext");
107 auto areDpiAwarenessContextsEqual =
108 (decltype(AreDpiAwarenessContextsEqual)*)::GetProcAddress(
109 user32Dll, "AreDpiAwarenessContextsEqual");
110 if (getThreadDpiAwarenessContext && areDpiAwarenessContextsEqual &&
111 areDpiAwarenessContextsEqual(
112 getThreadDpiAwarenessContext(),
113 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
114 // Only per-monitor v1 requires these workarounds.
115 sEnableNonClientDpiScaling =
116 (EnableNonClientDpiScalingProc)::GetProcAddress(
117 user32Dll, "EnableNonClientDpiScaling");
118 sSetThreadDpiAwarenessContext =
119 (SetThreadDpiAwarenessContextProc)::GetProcAddress(
120 user32Dll, "SetThreadDpiAwarenessContext");
123 sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)::GetProcAddress(
124 user32Dll, "GetSystemMetricsForDpi");
125 sGetDpiForWindow =
126 (GetDpiForWindowProc)::GetProcAddress(user32Dll, "GetDpiForWindow");
130 sHasPackageIdentity = mozilla::HasPackageIdentity();
133 // static
134 LRESULT WINAPI WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg,
135 WPARAM wParam,
136 LPARAM lParam) {
137 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
139 // NOTE: this function was copied out into the body of the pre-XUL skeleton
140 // UI window proc (PreXULSkeletonUI.cpp). If this function changes at any
141 // point, we should probably factor this out and use it from both locations.
142 if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) {
143 sEnableNonClientDpiScaling(hWnd);
145 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
148 // static
149 void WinUtils::LogW(const wchar_t* fmt, ...) {
150 va_list args = nullptr;
151 if (!lstrlenW(fmt)) {
152 return;
154 va_start(args, fmt);
155 int buflen = _vscwprintf(fmt, args);
156 wchar_t* buffer = new wchar_t[buflen + 1];
157 if (!buffer) {
158 va_end(args);
159 return;
161 vswprintf(buffer, buflen, fmt, args);
162 va_end(args);
164 // MSVC, including remote debug sessions
165 OutputDebugStringW(buffer);
166 OutputDebugStringW(L"\n");
168 int len =
169 WideCharToMultiByte(CP_ACP, 0, buffer, -1, nullptr, 0, nullptr, nullptr);
170 if (len) {
171 char* utf8 = new char[len];
172 if (WideCharToMultiByte(CP_ACP, 0, buffer, -1, utf8, len, nullptr,
173 nullptr) > 0) {
174 // desktop console
175 printf("%s\n", utf8);
176 NS_ASSERTION(gWindowsLog,
177 "Called WinUtils Log() but Widget "
178 "log module doesn't exist!");
179 MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", utf8));
181 delete[] utf8;
183 delete[] buffer;
186 // static
187 void WinUtils::Log(const char* fmt, ...) {
188 va_list args = nullptr;
189 if (!strlen(fmt)) {
190 return;
192 va_start(args, fmt);
193 int buflen = _vscprintf(fmt, args);
194 char* buffer = new char[buflen + 1];
195 if (!buffer) {
196 va_end(args);
197 return;
199 vsprintf(buffer, fmt, args);
200 va_end(args);
202 // MSVC, including remote debug sessions
203 OutputDebugStringA(buffer);
204 OutputDebugStringW(L"\n");
206 // desktop console
207 printf("%s\n", buffer);
209 NS_ASSERTION(gWindowsLog,
210 "Called WinUtils Log() but Widget "
211 "log module doesn't exist!");
212 MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", buffer));
213 delete[] buffer;
216 // static
217 float WinUtils::SystemDPI() {
218 // The result of GetDeviceCaps won't change dynamically, as it predates
219 // per-monitor DPI and support for on-the-fly resolution changes.
220 // Therefore, we only need to look it up once.
221 static float dpi = 0;
222 if (dpi <= 0) {
223 HDC screenDC = GetDC(nullptr);
224 dpi = GetDeviceCaps(screenDC, LOGPIXELSY);
225 ReleaseDC(nullptr, screenDC);
228 // Bug 1012487 - dpi can be 0 when the Screen DC is used off the
229 // main thread on windows. For now just assume a 100% DPI for this
230 // drawing call.
231 // XXX - fixme!
232 return dpi > 0 ? dpi : 96;
235 // static
236 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
238 typedef HRESULT(WINAPI* GETDPIFORMONITORPROC)(HMONITOR, MONITOR_DPI_TYPE, UINT*,
239 UINT*);
241 typedef HRESULT(WINAPI* GETPROCESSDPIAWARENESSPROC)(HANDLE,
242 PROCESS_DPI_AWARENESS*);
244 GETDPIFORMONITORPROC sGetDpiForMonitor;
245 GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness;
247 static bool SlowIsPerMonitorDPIAware() {
248 // Intentionally leak the handle.
249 HMODULE shcore = LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
250 if (shcore) {
251 sGetDpiForMonitor =
252 (GETDPIFORMONITORPROC)GetProcAddress(shcore, "GetDpiForMonitor");
253 sGetProcessDpiAwareness = (GETPROCESSDPIAWARENESSPROC)GetProcAddress(
254 shcore, "GetProcessDpiAwareness");
256 PROCESS_DPI_AWARENESS dpiAwareness;
257 return sGetDpiForMonitor && sGetProcessDpiAwareness &&
258 SUCCEEDED(
259 sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness)) &&
260 dpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE;
263 /* static */
264 bool WinUtils::IsPerMonitorDPIAware() {
265 static bool perMonitorDPIAware = SlowIsPerMonitorDPIAware();
266 return perMonitorDPIAware;
269 /* static */
270 float WinUtils::MonitorDPI(HMONITOR aMonitor) {
271 if (IsPerMonitorDPIAware()) {
272 UINT dpiX, dpiY = 96;
273 sGetDpiForMonitor(aMonitor ? aMonitor : GetPrimaryMonitor(),
274 MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
275 return dpiY;
278 // We're not per-monitor aware, use system DPI instead.
279 return SystemDPI();
282 /* static */
283 double WinUtils::LogToPhysFactor(HMONITOR aMonitor) {
284 return MonitorDPI(aMonitor) / 96.0;
287 /* static */
288 int32_t WinUtils::LogToPhys(HMONITOR aMonitor, double aValue) {
289 return int32_t(NS_round(aValue * LogToPhysFactor(aMonitor)));
292 /* static */
293 double WinUtils::LogToPhysFactor(HWND aWnd) {
294 // if there's an ancestor window, we want to share its DPI setting
295 HWND ancestor = ::GetAncestor(aWnd, GA_ROOTOWNER);
297 // The GetDpiForWindow api is not available everywhere where we run as
298 // per-monitor, but if it is available rely on it to tell us the scale
299 // factor of the window. See bug 1722085.
300 if (sGetDpiForWindow) {
301 UINT dpi = sGetDpiForWindow(ancestor ? ancestor : aWnd);
302 if (dpi > 0) {
303 return static_cast<double>(dpi) / 96.0;
306 return LogToPhysFactor(::MonitorFromWindow(ancestor ? ancestor : aWnd,
307 MONITOR_DEFAULTTOPRIMARY));
310 /* static */
311 HMONITOR
312 WinUtils::GetPrimaryMonitor() {
313 const POINT pt = {0, 0};
314 return ::MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
317 /* static */
318 HMONITOR
319 WinUtils::MonitorFromRect(const gfx::Rect& rect) {
320 // convert coordinates from desktop to device pixels for MonitorFromRect
321 double dpiScale =
322 IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor());
324 RECT globalWindowBounds = {NSToIntRound(dpiScale * rect.X()),
325 NSToIntRound(dpiScale * rect.Y()),
326 NSToIntRound(dpiScale * (rect.XMost())),
327 NSToIntRound(dpiScale * (rect.YMost()))};
329 return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST);
332 /* static */
333 bool WinUtils::HasSystemMetricsForDpi() {
334 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
335 return (sGetSystemMetricsForDpi != NULL);
338 /* static */
339 int WinUtils::GetSystemMetricsForDpi(int nIndex, UINT dpi) {
340 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
341 if (HasSystemMetricsForDpi()) {
342 return sGetSystemMetricsForDpi(nIndex, dpi);
343 } else {
344 double scale = IsPerMonitorDPIAware() ? dpi / SystemDPI() : 1.0;
345 return NSToIntRound(::GetSystemMetrics(nIndex) * scale);
349 /* static */
350 gfx::MarginDouble WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC aHdc) {
351 if (!aHdc) {
352 return gfx::MarginDouble();
355 int pixelsPerInchY = ::GetDeviceCaps(aHdc, LOGPIXELSY);
356 int marginTop = ::GetDeviceCaps(aHdc, PHYSICALOFFSETY);
357 int printableAreaHeight = ::GetDeviceCaps(aHdc, VERTRES);
358 int physicalHeight = ::GetDeviceCaps(aHdc, PHYSICALHEIGHT);
360 double marginTopInch = double(marginTop) / pixelsPerInchY;
362 double printableAreaHeightInch = double(printableAreaHeight) / pixelsPerInchY;
363 double physicalHeightInch = double(physicalHeight) / pixelsPerInchY;
364 double marginBottomInch =
365 physicalHeightInch - printableAreaHeightInch - marginTopInch;
367 int pixelsPerInchX = ::GetDeviceCaps(aHdc, LOGPIXELSX);
368 int marginLeft = ::GetDeviceCaps(aHdc, PHYSICALOFFSETX);
369 int printableAreaWidth = ::GetDeviceCaps(aHdc, HORZRES);
370 int physicalWidth = ::GetDeviceCaps(aHdc, PHYSICALWIDTH);
372 double marginLeftInch = double(marginLeft) / pixelsPerInchX;
374 double printableAreaWidthInch = double(printableAreaWidth) / pixelsPerInchX;
375 double physicalWidthInch = double(physicalWidth) / pixelsPerInchX;
376 double marginRightInch =
377 physicalWidthInch - printableAreaWidthInch - marginLeftInch;
379 return gfx::MarginDouble(marginTopInch, marginRightInch, marginBottomInch,
380 marginLeftInch);
383 #ifdef ACCESSIBILITY
384 /* static */
385 a11y::LocalAccessible* WinUtils::GetRootAccessibleForHWND(HWND aHwnd) {
386 nsWindow* window = GetNSWindowPtr(aHwnd);
387 if (!window) {
388 return nullptr;
391 return window->GetAccessible();
393 #endif // ACCESSIBILITY
395 /* static */
396 bool WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
397 UINT aLastMessage, UINT aOption) {
398 RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
399 if (msgPump) {
400 BOOL ret = FALSE;
401 HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
402 aOption, &ret);
403 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
404 return ret;
406 return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption);
409 /* static */
410 bool WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
411 UINT aLastMessage) {
412 RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
413 if (msgPump) {
414 BOOL ret = FALSE;
415 HRESULT hr =
416 msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, &ret);
417 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
418 return ret;
420 return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
423 #if defined(ACCESSIBILITY)
424 static DWORD GetWaitFlags() {
425 DWORD result = MWMO_INPUTAVAILABLE;
426 if (XRE_IsContentProcess()) {
427 result |= MWMO_ALERTABLE;
429 return result;
431 #endif
433 /* static */
434 void WinUtils::WaitForMessage(DWORD aTimeoutMs) {
435 #if defined(ACCESSIBILITY)
436 static const DWORD waitFlags = GetWaitFlags();
437 #else
438 const DWORD waitFlags = MWMO_INPUTAVAILABLE;
439 #endif
441 const DWORD waitStart = ::GetTickCount();
442 DWORD elapsed = 0;
443 while (true) {
444 if (aTimeoutMs != INFINITE) {
445 elapsed = ::GetTickCount() - waitStart;
447 if (elapsed >= aTimeoutMs) {
448 break;
450 DWORD result;
452 AUTO_PROFILER_THREAD_SLEEP;
453 result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
454 MOZ_QS_ALLEVENT, waitFlags);
456 NS_WARNING_ASSERTION(result != WAIT_FAILED, "Wait failed");
457 if (result == WAIT_TIMEOUT) {
458 break;
460 #if defined(ACCESSIBILITY)
461 if (result == WAIT_IO_COMPLETION) {
462 if (NS_IsMainThread()) {
463 // We executed an APC that would have woken up the hang monitor. Since
464 // there are no more APCs pending and we are now going to sleep again,
465 // we should notify the hang monitor.
466 mozilla::BackgroundHangMonitor().NotifyWait();
468 continue;
470 #endif // defined(ACCESSIBILITY)
472 // Sent messages (via SendMessage and friends) are processed differently
473 // than queued messages (via PostMessage); the destination window procedure
474 // of the sent message is called during (Get|Peek)Message. Since PeekMessage
475 // does not tell us whether it processed any sent messages, we need to query
476 // this ahead of time.
477 bool haveSentMessagesPending =
478 (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
480 MSG msg = {0};
481 if (haveSentMessagesPending ||
482 ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
483 break;
485 // The message is intended for another thread that has been synchronized
486 // with our input queue; yield to give other threads an opportunity to
487 // process the message. This should prevent busy waiting if resumed due
488 // to another thread's message.
489 ::SwitchToThread();
493 /* static */
494 HWND WinUtils::GetTopLevelHWND(HWND aWnd, bool aStopIfNotChild,
495 bool aStopIfNotPopup) {
496 HWND curWnd = aWnd;
497 HWND topWnd = nullptr;
499 while (curWnd) {
500 topWnd = curWnd;
502 if (aStopIfNotChild) {
503 DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE);
505 VERIFY_WINDOW_STYLE(style);
507 if (!(style & WS_CHILD)) // first top-level window
508 break;
511 HWND upWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
513 // GetParent will only return the owner if the passed in window
514 // has the WS_POPUP style.
515 if (!upWnd && !aStopIfNotPopup) {
516 upWnd = ::GetWindow(curWnd, GW_OWNER);
518 curWnd = upWnd;
521 return topWnd;
524 // Map from native window handles to nsWindow structures. Does not AddRef.
525 // Inherently unsafe to access outside the main thread.
526 MOZ_RUNINIT static nsTHashMap<HWND, nsWindow*> sExtantNSWindows;
528 /* static */
529 void WinUtils::SetNSWindowPtr(HWND aWnd, nsWindow* aWindow) {
530 MOZ_ASSERT(NS_IsMainThread());
531 if (!aWindow) {
532 sExtantNSWindows.Remove(aWnd);
533 } else {
534 sExtantNSWindows.InsertOrUpdate(aWnd, aWindow);
538 /* static */
539 nsWindow* WinUtils::GetNSWindowPtr(HWND aWnd) {
540 MOZ_ASSERT(NS_IsMainThread());
541 return sExtantNSWindows.Get(aWnd); // or nullptr
544 /* static */
545 bool WinUtils::IsOurProcessWindow(HWND aWnd) {
546 if (!aWnd) {
547 return false;
549 DWORD processId = 0;
550 ::GetWindowThreadProcessId(aWnd, &processId);
551 return (processId == ::GetCurrentProcessId());
554 /* static */
555 HWND WinUtils::FindOurProcessWindow(HWND aWnd) {
556 for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) {
557 if (IsOurProcessWindow(wnd)) {
558 return wnd;
561 return nullptr;
564 static bool IsPointInWindow(HWND aWnd, const POINT& aPointInScreen) {
565 RECT bounds;
566 if (!::GetWindowRect(aWnd, &bounds)) {
567 return false;
570 return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right &&
571 aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom);
575 * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
576 * forground in this context) of aWnd.
579 static HWND FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen) {
580 if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) {
581 return nullptr;
584 HWND childWnd = ::GetTopWindow(aWnd);
585 while (childWnd) {
586 HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen);
587 if (topmostWnd) {
588 return topmostWnd;
590 childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
593 return aWnd;
596 struct FindOurWindowAtPointInfo {
597 POINT mInPointInScreen;
598 HWND mOutWnd;
601 static BOOL CALLBACK FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM) {
602 if (!WinUtils::IsOurProcessWindow(aWnd)) {
603 // This isn't one of our top-level windows; continue enumerating.
604 return TRUE;
607 // Get the top-most child window under the point. If there's no child
608 // window, and the point is within the top-level window, then the top-level
609 // window will be returned. (This is the usual case. A child window
610 // would be returned for plugins.)
611 FindOurWindowAtPointInfo* info =
612 reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
613 HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen);
614 if (!childWnd) {
615 // This window doesn't contain the point; continue enumerating.
616 return TRUE;
619 // Return the HWND and stop enumerating.
620 info->mOutWnd = childWnd;
621 return FALSE;
624 /* static */
625 HWND WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen) {
626 FindOurWindowAtPointInfo info;
627 info.mInPointInScreen = aPointInScreen;
628 info.mOutWnd = nullptr;
630 // This will enumerate all top-level windows in order from top to bottom.
631 EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
632 return info.mOutWnd;
635 /* static */
636 UINT WinUtils::GetInternalMessage(UINT aNativeMessage) {
637 switch (aNativeMessage) {
638 case WM_MOUSEWHEEL:
639 return MOZ_WM_MOUSEVWHEEL;
640 case WM_MOUSEHWHEEL:
641 return MOZ_WM_MOUSEHWHEEL;
642 case WM_VSCROLL:
643 return MOZ_WM_VSCROLL;
644 case WM_HSCROLL:
645 return MOZ_WM_HSCROLL;
646 default:
647 return aNativeMessage;
651 /* static */
652 UINT WinUtils::GetNativeMessage(UINT aInternalMessage) {
653 switch (aInternalMessage) {
654 case MOZ_WM_MOUSEVWHEEL:
655 return WM_MOUSEWHEEL;
656 case MOZ_WM_MOUSEHWHEEL:
657 return WM_MOUSEHWHEEL;
658 case MOZ_WM_VSCROLL:
659 return WM_VSCROLL;
660 case MOZ_WM_HSCROLL:
661 return WM_HSCROLL;
662 default:
663 return aInternalMessage;
667 /* static */
668 uint16_t WinUtils::GetMouseInputSource() {
669 int32_t inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE;
670 LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
671 if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
672 inputSource = (lParamExtraInfo & TABLET_INK_TOUCH)
673 ? dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
674 : dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
676 return static_cast<uint16_t>(inputSource);
679 /* static */
680 uint16_t WinUtils::GetMousePointerID() {
681 LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
682 return lParamExtraInfo & TABLET_INK_ID_MASK;
685 /* static */
686 bool WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage) {
687 const uint32_t MOZ_T_I_SIGNATURE = TABLET_INK_TOUCH | TABLET_INK_SIGNATURE;
688 const uint32_t MOZ_T_I_CHECK_TCH = TABLET_INK_TOUCH | TABLET_INK_CHECK;
689 return ((aEventMessage == eMouseMove || aEventMessage == eMouseDown ||
690 aEventMessage == eMouseUp || aEventMessage == ePointerAuxClick ||
691 aEventMessage == eMouseDoubleClick) &&
692 (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE) == MOZ_T_I_CHECK_TCH);
695 /* static */
696 MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) {
697 MSG msg;
698 msg.message = aMessage;
699 msg.wParam = wParam;
700 msg.lParam = lParam;
701 msg.hwnd = aWnd;
702 return msg;
705 #ifdef MOZ_PLACES
706 /************************************************************************
707 * Constructs as AsyncFaviconDataReady Object
708 * @param aIOThread : the thread which performs the action
709 * @param aURLShortcut : Differentiates between (false)Jumplistcache and
710 * (true)Shortcutcache
711 * @param aRunnable : Executed in the aIOThread when the favicon cache is
712 * avaiable
713 * @param [aPromiseHolder=null]: Optional PromiseHolder that will be forwarded
714 * to AsyncEncodeAndWriteIcon if getting the
715 * favicon from the favicon service succeeds. If
716 * it doesn't succeed, the held MozPromise will
717 * be rejected.
718 ************************************************************************/
720 AsyncFaviconDataReady::AsyncFaviconDataReady(
721 nsIURI* aNewURI, RefPtr<nsISerialEventTarget> aIOThread,
722 const bool aURLShortcut, already_AddRefed<nsIRunnable> aRunnable,
723 UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
724 aPromiseHolder)
725 : mNewURI(aNewURI),
726 mIOThread(aIOThread),
727 mRunnable(aRunnable),
728 mPromiseHolder(std::move(aPromiseHolder)),
729 mURLShortcut(aURLShortcut) {}
731 NS_IMETHODIMP
732 myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
733 nsIRequest* request, nsresult status,
734 nsIFile* result) {
735 return NS_OK;
738 nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
739 if (!mURLShortcut) {
740 return NS_OK;
743 nsCOMPtr<nsIFile> icoFile;
744 nsresult rv =
745 FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
746 NS_ENSURE_SUCCESS(rv, rv);
748 nsCOMPtr<nsIURI> mozIconURI;
749 rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
750 if (NS_FAILED(rv)) {
751 return rv;
754 nsCOMPtr<nsIChannel> channel;
755 rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI,
756 nsContentUtils::GetSystemPrincipal(),
757 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
758 nsIContentPolicy::TYPE_INTERNAL_IMAGE);
760 NS_ENSURE_SUCCESS(rv, rv);
762 nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
763 nsCOMPtr<nsIStreamListener> listener;
764 rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
765 NS_ENSURE_SUCCESS(rv, rv);
767 return channel->AsyncOpen(listener);
770 NS_IMETHODIMP
771 AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
772 const uint8_t* aData,
773 const nsACString& aMimeType,
774 uint16_t aWidth) {
775 if (!aDataLen || !aData) {
776 if (mURLShortcut) {
777 OnFaviconDataNotAvailable();
780 return NS_OK;
783 nsCOMPtr<nsIFile> icoFile;
784 nsresult rv =
785 FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
786 NS_ENSURE_SUCCESS(rv, rv);
788 nsAutoString path;
789 rv = icoFile->GetPath(path);
790 NS_ENSURE_SUCCESS(rv, rv);
792 // Decode the image from the format it was returned to us in (probably PNG)
793 nsCOMPtr<imgIContainer> container;
794 nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
795 rv = imgtool->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData),
796 aDataLen, aMimeType,
797 getter_AddRefs(container));
798 NS_ENSURE_SUCCESS(rv, rv);
800 RefPtr<SourceSurface> surface = container->GetFrame(
801 imgIContainer::FRAME_FIRST,
802 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
803 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
805 RefPtr<DataSourceSurface> dataSurface;
806 IntSize size;
808 if (mURLShortcut &&
809 (surface->GetSize().width < 48 || surface->GetSize().height < 48)) {
810 // Create a 48x48 surface and paint the icon into the central rect.
811 size.width = std::max(surface->GetSize().width, 48);
812 size.height = std::max(surface->GetSize().height, 48);
813 dataSurface =
814 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
815 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
817 DataSourceSurface::MappedSurface map;
818 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
819 return NS_ERROR_FAILURE;
822 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
823 BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
824 dataSurface->GetFormat());
825 if (!dt) {
826 gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
827 "CreateDrawTargetForData";
828 return NS_ERROR_OUT_OF_MEMORY;
830 dt->FillRect(Rect(0, 0, size.width, size.height),
831 ColorPattern(ToDeviceColor(sRGBColor::OpaqueWhite())));
832 IntPoint point;
833 point.x = (size.width - surface->GetSize().width) / 2;
834 point.y = (size.height - surface->GetSize().height) / 2;
835 dt->DrawSurface(surface,
836 Rect(point.x, point.y, surface->GetSize().width,
837 surface->GetSize().height),
838 Rect(Point(0, 0), Size(surface->GetSize().width,
839 surface->GetSize().height)));
841 dataSurface->Unmap();
842 } else {
843 // By using the input image surface's size, we may end up encoding
844 // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
845 // Windows will resize appropriately for us. If we want to encode ourselves
846 // one day because we like our resizing better, we'd have to manually
847 // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
848 // SM_CYSMICON. We don't support resizing images asynchronously at the
849 // moment anyway so getting the DPI aware icon size won't help.
850 size.width = surface->GetSize().width;
851 size.height = surface->GetSize().height;
852 dataSurface = surface->GetDataSurface();
853 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
856 // Allocate a new buffer that we own and can use out of line in
857 // another thread.
858 UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface);
859 if (!data) {
860 return NS_ERROR_OUT_OF_MEMORY;
862 int32_t stride = 4 * size.width;
864 // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
865 nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(
866 path, std::move(data), stride, size.width, size.height,
867 mRunnable.forget(), std::move(mPromiseHolder));
868 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
870 return NS_OK;
872 #endif
874 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
875 // in
876 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
877 const nsAString& aIconPath, UniquePtr<uint8_t[]> aBuffer, uint32_t aStride,
878 uint32_t aWidth, uint32_t aHeight, already_AddRefed<nsIRunnable> aRunnable,
879 UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
880 aPromiseHolder)
881 : mIconPath(aIconPath),
882 mBuffer(std::move(aBuffer)),
883 mRunnable(aRunnable),
884 mPromiseHolder(std::move(aPromiseHolder)),
885 mStride(aStride),
886 mWidth(aWidth),
887 mHeight(aHeight) {}
889 NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
890 MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");
892 // Note that since we're off the main thread we can't use
893 // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
894 RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
895 mBuffer.get(), mStride, IntSize(mWidth, mHeight),
896 SurfaceFormat::B8G8R8A8);
898 FILE* file = _wfopen(mIconPath.get(), L"wb");
899 if (!file) {
900 // Maybe the directory doesn't exist; try creating it, then fopen again.
901 nsCOMPtr<nsIFile> comFile;
902 MOZ_TRY(NS_NewLocalFile(mIconPath, getter_AddRefs(comFile)));
903 nsCOMPtr<nsIFile> dirPath;
904 MOZ_TRY(comFile->GetParent(getter_AddRefs(dirPath)));
905 nsresult rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
906 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
907 return rv;
909 file = _wfopen(mIconPath.get(), L"wb");
910 if (!file) {
911 return NS_ERROR_FAILURE;
914 nsresult rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
915 gfxUtils::eBinaryEncode, file);
916 fclose(file);
917 NS_ENSURE_SUCCESS(rv, rv);
919 if (mRunnable) {
920 mRunnable->Run();
922 if (mPromiseHolder) {
923 mPromiseHolder->ResolveIfExists(mIconPath, __func__);
925 return rv;
928 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {
929 if (mPromiseHolder) {
930 mPromiseHolder->RejectIfExists(NS_ERROR_FAILURE, __func__);
934 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
935 bool aIgnoreRecent)
936 : mIgnoreRecent(aIgnoreRecent) {
937 // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
938 // threads, as it reads a pref, so cache its value here.
939 mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600;
941 // Prepare the profile directory cache on the main thread, to ensure we wont
942 // do this on non-main threads.
943 Unused << NS_GetSpecialDirectory("ProfLDS",
944 getter_AddRefs(mJumpListCacheDir));
947 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() {
948 if (!mJumpListCacheDir) {
949 return NS_ERROR_FAILURE;
951 // Construct the path of our jump list cache
952 nsresult rv = mJumpListCacheDir->AppendNative(
953 nsDependentCString(FaviconHelper::kJumpListCacheDir));
954 NS_ENSURE_SUCCESS(rv, rv);
956 nsCOMPtr<nsIDirectoryEnumerator> entries;
957 rv = mJumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
958 NS_ENSURE_SUCCESS(rv, rv);
960 // Loop through each directory entry and remove all ICO files found
961 do {
962 nsCOMPtr<nsIFile> currFile;
963 if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
964 break;
966 nsAutoString path;
967 if (NS_FAILED(currFile->GetPath(path))) continue;
969 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
970 // Check if the cached ICO file exists
971 bool exists;
972 if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
974 if (mIgnoreRecent) {
975 // Check to make sure the icon wasn't just recently created.
976 // If it was created recently, don't delete it yet.
977 int64_t fileModTime = 0;
978 rv = currFile->GetLastModifiedTime(&fileModTime);
979 fileModTime /= PR_MSEC_PER_SEC;
980 // If the icon is older than the regeneration time (+ 10 min to be
981 // safe), then it's old and we can get rid of it.
982 // This code is only hit directly after a regeneration.
983 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
984 if (NS_FAILED(rv) || (nowTime - fileModTime) < mIcoNoDeleteSeconds) {
985 continue;
989 // We found an ICO file that exists, so we should remove it
990 currFile->Remove(false);
992 } while (true);
994 return NS_OK;
997 AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() {}
1000 * (static) If the data is available, will return the path on disk where
1001 * the favicon for page aFaviconPageURI is stored. If the favicon does not
1002 * exist, or its cache is expired, this method will kick off an async request
1003 * for the icon so that next time the method is called it will be available.
1004 * @param aFaviconPageURI The URI of the page to obtain
1005 * @param aICOFilePath The path of the icon file
1006 * @param aIOThread The thread to perform the Fetch on
1007 * @param aURLShortcut to distinguish between jumplistcache(false) and
1008 * shortcutcache(true)
1009 * @param aRunnable Executed in the aIOThread when the favicon cache is
1010 * avaiable
1012 nsresult FaviconHelper::ObtainCachedIconFile(
1013 nsCOMPtr<nsIURI> aFaviconPageURI, nsString& aICOFilePath,
1014 RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut,
1015 already_AddRefed<nsIRunnable> aRunnable) {
1016 nsCOMPtr<nsIRunnable> runnable = aRunnable;
1017 // Obtain the ICO file path
1018 nsCOMPtr<nsIFile> icoFile;
1019 nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
1020 NS_ENSURE_SUCCESS(rv, rv);
1022 // Check if the cached ICO file already exists
1023 bool exists;
1024 rv = icoFile->Exists(&exists);
1025 NS_ENSURE_SUCCESS(rv, rv);
1027 if (exists) {
1028 // Obtain the file's last modification date in seconds
1029 int64_t fileModTime = 0;
1030 rv = icoFile->GetLastModifiedTime(&fileModTime);
1031 fileModTime /= PR_MSEC_PER_SEC;
1032 int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
1033 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1035 // If the last mod call failed or the icon is old then re-cache it
1036 // This check is in case the favicon of a page changes
1037 // the next time we try to build the jump list, the data will be available.
1038 if (NS_FAILED(rv) || (nowTime - fileModTime) > icoReCacheSecondsTimeout) {
1039 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1040 aURLShortcut, runnable.forget(),
1041 nullptr);
1042 return NS_ERROR_NOT_AVAILABLE;
1044 } else {
1045 // The file does not exist yet, obtain it async from the favicon service so
1046 // that the next time we try to build the jump list it'll be available.
1047 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1048 aURLShortcut, runnable.forget(), nullptr);
1049 return NS_ERROR_NOT_AVAILABLE;
1052 // The icoFile is filled with a path that exists, get its path
1053 rv = icoFile->GetPath(aICOFilePath);
1054 return rv;
1058 * (static)
1059 * Attempts to obtain a favicon from the nsIFaviconService and cache it on
1060 * disk in a place where Win32 utilities (like the jump list) can access them.
1062 * In the event that the favicon was cached recently, the returned MozPromise
1063 * will resolve with the cache path. If the cache is expired, it will be
1064 * refreshed before returning the path.
1066 * In the event that the favicon cannot be retrieved from the nsIFaviconService,
1067 * or something goes wrong writing the cache to disk, the returned MozPromise
1068 * will reject with an nsresult code.
1070 * This is similar to the ObtainCachedIconFile method, except that all IO
1071 * happens on the aIOThread rather than only some of the IO.
1073 * @param aFaviconPageURI
1074 * The URI of the page to obtain the favicon for.
1075 * @param aIOThread
1076 * The thread to perform the cache check and fetch/write on.
1077 * @param aCacheDir
1078 * Which cache directory to use for the returned favicon (see
1079 * FaviconHelper::IconCacheDir).
1080 * @returns {RefPtr<ObtainCachedIconFileAsyncPromise>}
1081 * Resolves with the path of the cached favicon, or rejects with an nsresult
1082 * in the event that the favicon cannot be retrieved and/or cached.
1084 auto FaviconHelper::ObtainCachedIconFileAsync(
1085 nsCOMPtr<nsIURI> aFaviconPageURI, RefPtr<LazyIdleThread>& aIOThread,
1086 FaviconHelper::IconCacheDir aCacheDir)
1087 -> RefPtr<ObtainCachedIconFileAsyncPromise> {
1088 bool useShortcutCacheDir =
1089 aCacheDir == FaviconHelper::IconCacheDir::ShortcutCacheDir;
1091 // Obtain the file path that the ICO, if it exists, is expected to be
1092 // at for aFaviconPageURI
1093 nsCOMPtr<nsIFile> icoFile;
1094 nsresult rv =
1095 GetOutputIconPath(aFaviconPageURI, icoFile, useShortcutCacheDir);
1096 if (NS_FAILED(rv)) {
1097 return ObtainCachedIconFileAsyncPromise::CreateAndReject(rv, __func__);
1100 int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
1102 return InvokeAsync(
1103 aIOThread, "FaviconHelper::ObtainCachedIconFileAsync disk cache check",
1104 [icoFile = std::move(icoFile), icoReCacheSecondsTimeout,
1105 pageURI = std::move(aFaviconPageURI), useShortcutCacheDir]() {
1106 MOZ_ASSERT(!NS_IsMainThread());
1107 bool exists;
1108 nsresult rv = icoFile->Exists(&exists);
1110 if (NS_FAILED(rv)) {
1111 return ObtainCachedIconFileAsyncPromise::CreateAndReject(
1112 rv, "ObtainCachedIconFileAsync disk cache check: exists failed");
1115 if (exists) {
1116 // Obtain the file's last modification date in seconds
1117 int64_t fileModTime = 0;
1118 rv = icoFile->GetLastModifiedTime(&fileModTime);
1119 fileModTime /= PR_MSEC_PER_SEC;
1120 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1122 // If the file seems to be less than icoReCacheSecondsTimeout old,
1123 // then we can go ahead and return its path on the filesystem.
1124 if (NS_SUCCEEDED(rv) &&
1125 (nowTime - fileModTime) < icoReCacheSecondsTimeout) {
1126 nsAutoString icoFilePath;
1127 rv = icoFile->GetPath(icoFilePath);
1128 if (NS_SUCCEEDED(rv)) {
1129 return ObtainCachedIconFileAsyncPromise::CreateAndResolve(
1130 icoFilePath,
1131 "ObtainCachedIconFileAsync disk cache check: found");
1136 // Now dispatch a runnable to the main thread that will call
1137 // CacheIconFileFromFaviconURIAsync to request the favicon.
1138 RefPtr<nsISerialEventTarget> currentThread =
1139 GetCurrentSerialEventTarget();
1141 return InvokeAsync(
1142 GetMainThreadSerialEventTarget(),
1143 "ObtainCachedIconFileAsync call to "
1144 "PromiseCacheIconFileFromFaviconURIAsync",
1145 [useShortcutCacheDir, pageURI = std::move(pageURI),
1146 icoFile = std::move(icoFile),
1147 aIOThread = std::move(currentThread)]() {
1148 auto holder = MakeUnique<
1149 MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>();
1150 RefPtr<ObtainCachedIconFileAsyncPromise> promise =
1151 holder->Ensure(__func__);
1152 CacheIconFileFromFaviconURIAsync(pageURI, icoFile, aIOThread,
1153 useShortcutCacheDir, nullptr,
1154 std::move(holder));
1155 return promise;
1160 // Hash a URI using a cryptographic hash function (currently SHA-256)
1161 // Output will be a base64-encoded string of the hash.
1162 static nsresult HashURI(nsIURI* aUri, nsACString& aUriHash) {
1163 nsAutoCString spec;
1164 nsresult rv = aUri->GetSpec(spec);
1165 NS_ENSURE_SUCCESS(rv, rv);
1167 nsCOMPtr<nsICryptoHash> cryptoHash =
1168 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1169 NS_ENSURE_SUCCESS(rv, rv);
1171 rv = cryptoHash->Init(nsICryptoHash::SHA256);
1172 NS_ENSURE_SUCCESS(rv, rv);
1174 // Add some context to the hash to even further reduce the chances of
1175 // collision. Note that we are hashing this string with its null-terminator.
1176 const char kHashUriContext[] = "firefox-uri";
1177 rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(kHashUriContext),
1178 sizeof(kHashUriContext));
1179 NS_ENSURE_SUCCESS(rv, rv);
1181 rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(spec.BeginReading()),
1182 spec.Length());
1183 NS_ENSURE_SUCCESS(rv, rv);
1185 rv = cryptoHash->Finish(true, aUriHash);
1186 NS_ENSURE_SUCCESS(rv, rv);
1188 return NS_OK;
1191 // (static) Obtains the ICO file for the favicon at page aFaviconPageURI
1192 // If successful, the file path on disk is in the format:
1193 // <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
1195 // We generate the name with a cryptographically secure hash function in order
1196 // to ensure that malicious websites can't intentionally craft URLs to collide
1197 // with legitimate websites.
1198 nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
1199 nsCOMPtr<nsIFile>& aICOFile,
1200 bool aURLShortcut) {
1201 nsAutoCString inputURIHash;
1202 nsresult rv = HashURI(aFaviconPageURI, inputURIHash);
1203 NS_ENSURE_SUCCESS(rv, rv);
1204 char* cur = inputURIHash.BeginWriting();
1205 char* end = inputURIHash.EndWriting();
1206 for (; cur < end; ++cur) {
1207 if ('/' == *cur) {
1208 *cur = '_';
1212 // Obtain the local profile directory and construct the output icon file path
1213 rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
1214 NS_ENSURE_SUCCESS(rv, rv);
1215 if (!aURLShortcut)
1216 rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
1217 else
1218 rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
1219 NS_ENSURE_SUCCESS(rv, rv);
1221 // Append the icon extension
1222 inputURIHash.AppendLiteral(".ico");
1223 rv = aICOFile->AppendNative(inputURIHash);
1225 return rv;
1228 // (static) Asynchronously creates a cached ICO file on disk for the favicon of
1229 // page aFaviconPageURI and stores it to disk at the path of aICOFile.
1230 nsresult FaviconHelper::CacheIconFileFromFaviconURIAsync(
1231 nsCOMPtr<nsIURI> aFaviconPageURI, nsCOMPtr<nsIFile> aICOFile,
1232 RefPtr<nsISerialEventTarget> aIOThread, bool aURLShortcut,
1233 already_AddRefed<nsIRunnable> aRunnable,
1234 UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
1235 aPromiseHolder) {
1236 MOZ_ASSERT(NS_IsMainThread());
1237 nsCOMPtr<nsIRunnable> runnable = aRunnable;
1238 #ifdef MOZ_PLACES
1239 // Obtain the favicon service and get the favicon for the specified page
1240 nsCOMPtr<nsIFaviconService> favIconSvc(
1241 do_GetService("@mozilla.org/browser/favicon-service;1"));
1242 NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
1244 nsCOMPtr<nsIFaviconDataCallback> callback =
1245 new mozilla::widget::AsyncFaviconDataReady(
1246 aFaviconPageURI, aIOThread, aURLShortcut, runnable.forget(),
1247 std::move(aPromiseHolder));
1249 favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
1250 #endif
1251 return NS_OK;
1254 // Obtains the jump list 'ICO cache timeout in seconds' pref
1255 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
1256 // Only obtain the setting at most once from the pref service.
1257 // In the rare case that 2 threads call this at the same
1258 // time it is no harm and we will simply obtain the pref twice.
1259 // None of the taskbar list prefs are currently updated via a
1260 // pref observer so I think this should suffice.
1261 const int32_t kSecondsPerDay = 86400;
1262 static bool alreadyObtained = false;
1263 static int32_t icoReCacheSecondsTimeout = kSecondsPerDay;
1264 if (alreadyObtained) {
1265 return icoReCacheSecondsTimeout;
1268 // Obtain the pref
1269 const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1270 icoReCacheSecondsTimeout =
1271 Preferences::GetInt(PREF_ICOTIMEOUT, kSecondsPerDay);
1272 alreadyObtained = true;
1273 return icoReCacheSecondsTimeout;
1276 /* static */
1277 LayoutDeviceIntRegion WinUtils::ConvertHRGNToRegion(HRGN aRgn) {
1278 NS_ASSERTION(aRgn, "Don't pass NULL region here");
1280 LayoutDeviceIntRegion rgn;
1282 DWORD size = ::GetRegionData(aRgn, 0, nullptr);
1283 AutoTArray<uint8_t, 100> buffer;
1284 buffer.SetLength(size);
1286 RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements());
1287 if (!::GetRegionData(aRgn, size, data)) return rgn;
1289 if (data->rdh.nCount > MAX_RECTS_IN_REGION) {
1290 rgn = ToIntRect(data->rdh.rcBound);
1291 return rgn;
1294 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
1295 for (uint32_t i = 0; i < data->rdh.nCount; ++i) {
1296 RECT* r = rects + i;
1297 rgn.Or(rgn, ToIntRect(*r));
1300 return rgn;
1303 /* static */
1304 nsAutoRegion WinUtils::RegionToHRGN(const LayoutDeviceIntRegion& aRegion) {
1305 const uint32_t count = aRegion.GetNumRects();
1306 const size_t regionBytes = count * sizeof(RECT);
1307 const size_t regionDataBytes = sizeof(RGNDATAHEADER) + regionBytes;
1308 // See:
1309 // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-rgndataheader
1310 // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-extcreateregion
1311 auto buffer = MakeUnique<char[]>(regionDataBytes);
1312 auto* data = reinterpret_cast<RGNDATA*>(buffer.get());
1313 data->rdh.dwSize = sizeof(RGNDATAHEADER);
1314 data->rdh.iType = RDH_RECTANGLES;
1315 data->rdh.nCount = count;
1316 data->rdh.nRgnSize = regionBytes;
1317 data->rdh.rcBound = ToWinRect(aRegion.GetBounds());
1318 RECT* buf = (RECT*)data->Buffer;
1319 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
1320 *buf++ = ToWinRect(iter.Get());
1322 return nsAutoRegion(::ExtCreateRegion(nullptr, regionDataBytes, data));
1325 LayoutDeviceIntRect WinUtils::ToIntRect(const RECT& aRect) {
1326 return LayoutDeviceIntRect(aRect.left, aRect.top, aRect.right - aRect.left,
1327 aRect.bottom - aRect.top);
1330 RECT WinUtils::ToWinRect(const LayoutDeviceIntRect& aRect) {
1331 return {
1332 .left = aRect.x,
1333 .top = aRect.y,
1334 .right = aRect.XMost(),
1335 .bottom = aRect.YMost(),
1339 /* static */
1340 bool WinUtils::IsIMEEnabled(const InputContext& aInputContext) {
1341 return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
1344 /* static */
1345 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState) {
1346 return aIMEState == IMEEnabled::Enabled;
1349 /* static */
1350 void WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray,
1351 uint32_t aModifiers, UINT aMessage) {
1352 MOZ_ASSERT(!(aModifiers & nsIWidget::ALTGRAPH) ||
1353 !(aModifiers & (nsIWidget::CTRL_L | nsIWidget::ALT_R)));
1354 if (aMessage == WM_KEYUP) {
1355 // If AltGr is released, ControlLeft key is released first, then,
1356 // AltRight key is released.
1357 if (aModifiers & nsIWidget::ALTGRAPH) {
1358 aArray->AppendElement(
1359 KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1360 aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1362 for (uint32_t i = std::size(sModifierKeyMap); i; --i) {
1363 const uint32_t* map = sModifierKeyMap[i - 1];
1364 if (aModifiers & map[0]) {
1365 aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1368 } else {
1369 for (uint32_t i = 0; i < std::size(sModifierKeyMap); ++i) {
1370 const uint32_t* map = sModifierKeyMap[i];
1371 if (aModifiers & map[0]) {
1372 aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1375 // If AltGr is pressed, ControlLeft key is pressed first, then,
1376 // AltRight key is pressed.
1377 if (aModifiers & nsIWidget::ALTGRAPH) {
1378 aArray->AppendElement(
1379 KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1380 aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1385 /* static */
1386 nsresult WinUtils::WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
1387 RefPtr<SourceSurface> surface = aImage->GetFrame(
1388 imgIContainer::FRAME_FIRST,
1389 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
1390 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
1392 return WriteBitmap(aFile, surface);
1395 /* static */
1396 nsresult WinUtils::WriteBitmap(nsIFile* aFile, SourceSurface* surface) {
1397 nsresult rv;
1399 // For either of the following formats we want to set the biBitCount member
1400 // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
1401 // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
1402 // for the BI_RGB value we use for the biCompression member.
1403 MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
1404 surface->GetFormat() == SurfaceFormat::B8G8R8X8);
1406 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
1407 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
1409 int32_t width = dataSurface->GetSize().width;
1410 int32_t height = dataSurface->GetSize().height;
1411 int32_t bytesPerPixel = 4 * sizeof(uint8_t);
1412 uint32_t bytesPerRow = bytesPerPixel * width;
1413 bool hasAlpha = surface->GetFormat() == SurfaceFormat::B8G8R8A8;
1415 // initialize these bitmap structs which we will later
1416 // serialize directly to the head of the bitmap file
1417 BITMAPV4HEADER bmi;
1418 memset(&bmi, 0, sizeof(BITMAPV4HEADER));
1419 bmi.bV4Size = sizeof(BITMAPV4HEADER);
1420 bmi.bV4Width = width;
1421 bmi.bV4Height = height;
1422 bmi.bV4Planes = 1;
1423 bmi.bV4BitCount = (WORD)bytesPerPixel * 8;
1424 bmi.bV4V4Compression = hasAlpha ? BI_BITFIELDS : BI_RGB;
1425 bmi.bV4SizeImage = bytesPerRow * height;
1426 bmi.bV4CSType = LCS_sRGB;
1427 if (hasAlpha) {
1428 bmi.bV4RedMask = 0x00FF0000;
1429 bmi.bV4GreenMask = 0x0000FF00;
1430 bmi.bV4BlueMask = 0x000000FF;
1431 bmi.bV4AlphaMask = 0xFF000000;
1434 BITMAPFILEHEADER bf;
1435 DWORD colormask[3];
1436 bf.bfType = 0x4D42; // 'BM'
1437 bf.bfReserved1 = 0;
1438 bf.bfReserved2 = 0;
1439 bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) +
1440 (hasAlpha ? sizeof(colormask) : 0);
1441 bf.bfSize = bf.bfOffBits + bmi.bV4SizeImage;
1443 // get a file output stream
1444 nsCOMPtr<nsIOutputStream> stream;
1445 rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
1446 NS_ENSURE_SUCCESS(rv, rv);
1448 DataSourceSurface::MappedSurface map;
1449 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
1450 return NS_ERROR_FAILURE;
1453 // write the bitmap headers and rgb pixel data to the file
1454 rv = NS_ERROR_FAILURE;
1455 if (stream) {
1456 uint32_t written;
1457 stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
1458 if (written == sizeof(BITMAPFILEHEADER)) {
1459 stream->Write((const char*)&bmi, sizeof(BITMAPV4HEADER), &written);
1460 if (written == sizeof(BITMAPV4HEADER)) {
1461 if (hasAlpha) {
1462 // color mask
1463 colormask[0] = 0x00FF0000;
1464 colormask[1] = 0x0000FF00;
1465 colormask[2] = 0x000000FF;
1467 stream->Write((const char*)colormask, sizeof(colormask), &written);
1469 if (!hasAlpha || written == sizeof(colormask)) {
1470 // write out the image data backwards because the desktop won't
1471 // show bitmaps with negative heights for top-to-bottom
1472 uint32_t i = map.mStride * height;
1473 do {
1474 i -= map.mStride;
1475 stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
1476 if (written == bytesPerRow) {
1477 rv = NS_OK;
1478 } else {
1479 rv = NS_ERROR_FAILURE;
1480 break;
1482 } while (i != 0);
1487 stream->Close();
1490 dataSurface->Unmap();
1492 return rv;
1495 // This is in use here and in dom/events/TouchEvent.cpp
1496 /* static */
1497 uint32_t WinUtils::IsTouchDeviceSupportPresent() {
1498 int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
1499 int32_t touchFlags = NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH;
1500 if (StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled()) {
1501 touchFlags |= NID_EXTERNAL_PEN | NID_INTEGRATED_PEN;
1503 return (touchCapabilities & NID_READY) && (touchCapabilities & touchFlags);
1506 /* static */
1507 uint32_t WinUtils::GetMaxTouchPoints() {
1508 if (IsTouchDeviceSupportPresent()) {
1509 return GetSystemMetrics(SM_MAXIMUMTOUCHES);
1511 return 0;
1514 /* static */
1515 POWER_PLATFORM_ROLE
1516 WinUtils::GetPowerPlatformRole() {
1517 typedef POWER_PLATFORM_ROLE(WINAPI *
1518 PowerDeterminePlatformRoleEx)(ULONG Version);
1519 static PowerDeterminePlatformRoleEx power_determine_platform_role =
1520 reinterpret_cast<PowerDeterminePlatformRoleEx>(::GetProcAddress(
1521 ::LoadLibraryW(L"PowrProf.dll"), "PowerDeterminePlatformRoleEx"));
1523 POWER_PLATFORM_ROLE powerPlatformRole = PlatformRoleUnspecified;
1524 if (!power_determine_platform_role) {
1525 return powerPlatformRole;
1528 return power_determine_platform_role(POWER_PLATFORM_ROLE_V2);
1531 // static
1532 bool WinUtils::GetAutoRotationState(AR_STATE* aRotationState) {
1533 typedef BOOL(WINAPI * GetAutoRotationStateFunc)(PAR_STATE pState);
1534 static GetAutoRotationStateFunc get_auto_rotation_state_func =
1535 reinterpret_cast<GetAutoRotationStateFunc>(::GetProcAddress(
1536 GetModuleHandleW(L"user32.dll"), "GetAutoRotationState"));
1537 if (get_auto_rotation_state_func) {
1538 ZeroMemory(aRotationState, sizeof(AR_STATE));
1539 return get_auto_rotation_state_func(aRotationState);
1541 return false;
1544 // static
1545 void WinUtils::GetClipboardFormatAsString(UINT aFormat, nsAString& aOutput) {
1546 wchar_t buf[256] = {};
1547 // Get registered format name and ensure the existence of a terminating '\0'
1548 // if the registered name is more than 256 characters.
1549 if (::GetClipboardFormatNameW(aFormat, buf, ARRAYSIZE(buf) - 1)) {
1550 aOutput.Append(buf);
1551 return;
1553 // Standard clipboard formats
1554 // https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
1555 switch (aFormat) {
1556 case CF_TEXT: // 1
1557 aOutput.Append(u"CF_TEXT"_ns);
1558 break;
1559 case CF_BITMAP: // 2
1560 aOutput.Append(u"CF_BITMAP"_ns);
1561 break;
1562 case CF_DIB: // 8
1563 aOutput.Append(u"CF_DIB"_ns);
1564 break;
1565 case CF_UNICODETEXT: // 13
1566 aOutput.Append(u"CF_UNICODETEXT"_ns);
1567 break;
1568 case CF_HDROP: // 15
1569 aOutput.Append(u"CF_HDROP"_ns);
1570 break;
1571 case CF_DIBV5: // 17
1572 aOutput.Append(u"CF_DIBV5"_ns);
1573 break;
1574 default:
1575 aOutput.AppendPrintf("%u", aFormat);
1576 break;
1580 static bool IsTabletDevice() {
1581 // Guarantees that:
1582 // - The device has a touch screen.
1583 // - It is used as a tablet which means that it has no keyboard connected.
1585 if (WindowsUIUtils::GetInWin10TabletMode()) {
1586 return true;
1589 if (!GetSystemMetrics(SM_MAXIMUMTOUCHES)) {
1590 return false;
1593 // If the device is docked, the user is treating the device as a PC.
1594 if (GetSystemMetrics(SM_SYSTEMDOCKED)) {
1595 return false;
1598 // If the device is not supporting rotation, it's unlikely to be a tablet,
1599 // a convertible or a detachable. See:
1600 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
1601 AR_STATE rotation_state;
1602 if (WinUtils::GetAutoRotationState(&rotation_state) &&
1603 (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR))) {
1604 return false;
1607 // PlatformRoleSlate was added in Windows 8+.
1608 POWER_PLATFORM_ROLE role = WinUtils::GetPowerPlatformRole();
1609 if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
1610 return !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
1612 return false;
1615 bool WinUtils::SystemHasMouse() {
1616 // As per MSDN, this value is rarely false because of virtual mice, and
1617 // some machines report the existance of a mouse port as a mouse.
1619 // We probably could try to distinguish if we wanted, but a virtual mouse
1620 // might be there for a reason, and maybe we shouldn't assume we know
1621 // better.
1622 return !!::GetSystemMetrics(SM_MOUSEPRESENT);
1625 /* static */
1626 PointerCapabilities WinUtils::GetPrimaryPointerCapabilities() {
1627 if (IsTabletDevice()) {
1628 return PointerCapabilities::Coarse;
1631 if (SystemHasMouse()) {
1632 return PointerCapabilities::Fine | PointerCapabilities::Hover;
1635 if (IsTouchDeviceSupportPresent()) {
1636 return PointerCapabilities::Coarse;
1639 return PointerCapabilities::None;
1642 bool WinUtils::SystemHasTouch() {
1643 int digitizerMetrics = ::GetSystemMetrics(SM_DIGITIZER);
1644 return (digitizerMetrics & NID_INTEGRATED_TOUCH) ||
1645 (digitizerMetrics & NID_EXTERNAL_TOUCH);
1648 bool WinUtils::SystemHasPen() {
1649 int digitizerMetrics = ::GetSystemMetrics(SM_DIGITIZER);
1650 return (digitizerMetrics & NID_INTEGRATED_PEN) ||
1651 (digitizerMetrics & NID_EXTERNAL_PEN);
1654 /* static */
1655 PointerCapabilities WinUtils::GetAllPointerCapabilities() {
1656 PointerCapabilities pointerCapabilities = PointerCapabilities::None;
1658 if (SystemHasTouch()) {
1659 pointerCapabilities |= PointerCapabilities::Coarse;
1662 if (SystemHasPen() || SystemHasMouse()) {
1663 pointerCapabilities |=
1664 PointerCapabilities::Fine | PointerCapabilities::Hover;
1667 return pointerCapabilities;
1670 /* static */
1671 bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath) {
1672 static mozilla::LazyLogModule sNTFSLog("NTFS");
1674 MOZ_LOG(
1675 sNTFSLog, LogLevel::Debug,
1676 ("ResolveJunctionPointsAndSymLinks: Resolving path: %S", aPath.c_str()));
1678 wchar_t path[MAX_PATH] = {0};
1680 nsAutoHandle handle(::CreateFileW(
1681 aPath.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1682 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
1684 if (handle == INVALID_HANDLE_VALUE) {
1685 MOZ_LOG(sNTFSLog, LogLevel::Error,
1686 ("Failed to open file handle to resolve path. GetLastError=%lu",
1687 GetLastError()));
1688 return false;
1691 DWORD pathLen = GetFinalPathNameByHandleW(
1692 handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1693 if (pathLen == 0 || pathLen >= MAX_PATH) {
1694 MOZ_LOG(
1695 sNTFSLog, LogLevel::Error,
1696 ("GetFinalPathNameByHandleW failed. GetLastError=%lu", GetLastError()));
1697 return false;
1699 aPath = path;
1701 // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
1702 // but that confuses some APIs so strip it off. It will also put
1703 // '\\?\UNC\' in front of network paths, we convert that to '\\'.
1704 if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
1705 aPath.erase(2, 6);
1706 } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
1707 aPath.erase(0, 4);
1710 MOZ_LOG(sNTFSLog, LogLevel::Debug,
1711 ("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
1712 aPath.c_str()));
1713 return true;
1716 /* static */
1717 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile* aPath) {
1718 MOZ_ASSERT(aPath);
1720 nsAutoString filePath;
1721 nsresult rv = aPath->GetPath(filePath);
1722 if (NS_WARN_IF(NS_FAILED(rv))) {
1723 return false;
1726 std::wstring resolvedPath(filePath.get());
1727 if (!ResolveJunctionPointsAndSymLinks(resolvedPath)) {
1728 return false;
1731 rv = aPath->InitWithPath(nsDependentString(resolvedPath.c_str()));
1732 if (NS_WARN_IF(NS_FAILED(rv))) {
1733 return false;
1736 return true;
1739 /* static */
1740 bool WinUtils::RunningFromANetworkDrive() {
1741 wchar_t exePath[MAX_PATH];
1742 if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) {
1743 return false;
1746 std::wstring exeString(exePath);
1747 if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString)) {
1748 return false;
1751 wchar_t volPath[MAX_PATH];
1752 if (!::GetVolumePathNameW(exeString.c_str(), volPath, MAX_PATH)) {
1753 return false;
1756 return (::GetDriveTypeW(volPath) == DRIVE_REMOTE);
1759 /* static */
1760 bool WinUtils::CanonicalizePath(nsAString& aPath) {
1761 wchar_t tempPath[MAX_PATH + 1];
1762 if (!PathCanonicalizeW(tempPath,
1763 (char16ptr_t)PromiseFlatString(aPath).get())) {
1764 return false;
1766 aPath = tempPath;
1767 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1768 return true;
1771 /* static */
1772 bool WinUtils::MakeLongPath(nsAString& aPath) {
1773 wchar_t tempPath[MAX_PATH + 1];
1774 DWORD longResult =
1775 GetLongPathNameW((char16ptr_t)PromiseFlatString(aPath).get(), tempPath,
1776 std::size(tempPath));
1777 if (longResult > std::size(tempPath)) {
1778 // Our buffer is too short, and we're guaranteeing <= MAX_PATH results.
1779 return false;
1780 } else if (longResult) {
1781 // Success.
1782 aPath = tempPath;
1783 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1785 // GetLongPathNameW returns 0 if the path is not found or is not rooted,
1786 // but we shouldn't consider that a failure condition.
1787 return true;
1790 /* static */
1791 bool WinUtils::UnexpandEnvVars(nsAString& aPath) {
1792 wchar_t tempPath[MAX_PATH + 1];
1793 // PathUnExpandEnvStringsW returns false if it doesn't make any
1794 // substitutions. Silently continue using the unaltered path.
1795 if (PathUnExpandEnvStringsW((char16ptr_t)PromiseFlatString(aPath).get(),
1796 tempPath, std::size(tempPath))) {
1797 aPath = tempPath;
1798 MOZ_ASSERT(aPath.Length() <= MAX_PATH);
1800 return true;
1803 /* static */
1804 WinUtils::WhitelistVec WinUtils::BuildWhitelist() {
1805 WhitelistVec result;
1807 Unused << result.emplaceBack(
1808 std::make_pair(nsString(u"%ProgramFiles%"_ns), nsDependentString()));
1810 // When no substitution is required, set the void flag
1811 result.back().second.SetIsVoid(true);
1813 Unused << result.emplaceBack(
1814 std::make_pair(nsString(u"%SystemRoot%"_ns), nsDependentString()));
1815 result.back().second.SetIsVoid(true);
1817 wchar_t tmpPath[MAX_PATH + 1] = {};
1818 if (GetTempPath(MAX_PATH, tmpPath)) {
1819 // GetTempPath's result always ends with a backslash, which we don't want
1820 uint32_t tmpPathLen = wcslen(tmpPath);
1821 if (tmpPathLen) {
1822 tmpPath[tmpPathLen - 1] = 0;
1825 nsAutoString cleanTmpPath(tmpPath);
1826 if (UnexpandEnvVars(cleanTmpPath)) {
1827 constexpr auto tempVar = u"%TEMP%"_ns;
1828 Unused << result.emplaceBack(std::make_pair(
1829 nsString(cleanTmpPath), nsDependentString(tempVar, 0)));
1833 // If we add more items to the whitelist, ensure we still don't invoke an
1834 // unnecessary heap allocation.
1835 MOZ_ASSERT(result.length() <= kMaxWhitelistedItems);
1837 return result;
1841 * This function provides an array of (system path, substitution) pairs that are
1842 * considered to be acceptable with respect to privacy, for the purposes of
1843 * submitting within telemetry or crash reports.
1845 * The substitution string's void flag may be set. If it is, no subsitution is
1846 * necessary. Otherwise, the consumer should replace the system path with the
1847 * substitution.
1849 * @see PreparePathForTelemetry for an example of its usage.
1851 /* static */
1852 const WinUtils::WhitelistVec& WinUtils::GetWhitelistedPaths() {
1853 static WhitelistVec sWhitelist([]() -> WhitelistVec {
1854 auto setClearFn = [ptr = &sWhitelist]() -> void {
1855 RunOnShutdown([ptr]() -> void { ptr->clear(); },
1856 ShutdownPhase::XPCOMShutdownFinal);
1859 if (NS_IsMainThread()) {
1860 setClearFn();
1861 } else {
1862 SchedulerGroup::Dispatch(NS_NewRunnableFunction(
1863 "WinUtils::GetWhitelistedPaths", std::move(setClearFn)));
1866 return BuildWhitelist();
1867 }());
1868 return sWhitelist;
1872 * This function is located here (as opposed to nsSystemInfo or elsewhere)
1873 * because we need to gather this information as early as possible during
1874 * startup.
1876 /* static */
1877 bool WinUtils::GetAppInitDLLs(nsAString& aOutput) {
1878 aOutput.Truncate();
1879 HKEY hkey = NULL;
1880 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1881 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
1882 0, KEY_QUERY_VALUE, &hkey)) {
1883 return false;
1885 nsAutoRegKey key(hkey);
1886 LONG status;
1887 const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
1888 DWORD loadAppInitDLLs = 0;
1889 DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
1890 status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr, nullptr,
1891 (LPBYTE)&loadAppInitDLLs, &loadAppInitDLLsLen);
1892 if (status != ERROR_SUCCESS) {
1893 return false;
1895 if (!loadAppInitDLLs) {
1896 // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
1897 // In this case we'll return true along with an empty output string.
1898 return true;
1900 DWORD numBytes = 0;
1901 const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
1902 // Query for required buffer size
1903 status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr,
1904 &numBytes);
1905 if (status != ERROR_SUCCESS) {
1906 return false;
1908 // Allocate the buffer and query for the actual data
1909 mozilla::UniquePtr<wchar_t[]> data =
1910 mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
1911 status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr,
1912 (LPBYTE)data.get(), &numBytes);
1913 if (status != ERROR_SUCCESS) {
1914 return false;
1916 // For each token, split up the filename components and then check the
1917 // name of the file.
1918 const wchar_t kDelimiters[] = L", ";
1919 wchar_t* tokenContext = nullptr;
1920 wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
1921 while (token) {
1922 nsAutoString cleanPath(token);
1923 // Since these paths are short paths originating from the registry, we need
1924 // to canonicalize them, lengthen them, and sanitize them before we can
1925 // check them against the whitelist
1926 if (PreparePathForTelemetry(cleanPath)) {
1927 if (!aOutput.IsEmpty()) {
1928 aOutput += L";";
1930 aOutput += cleanPath;
1932 token = wcstok_s(nullptr, kDelimiters, &tokenContext);
1934 return true;
1937 /* static */
1938 bool WinUtils::PreparePathForTelemetry(nsAString& aPath,
1939 PathTransformFlags aFlags) {
1940 if (aFlags & PathTransformFlags::Canonicalize) {
1941 if (!CanonicalizePath(aPath)) {
1942 return false;
1945 if (aFlags & PathTransformFlags::Lengthen) {
1946 if (!MakeLongPath(aPath)) {
1947 return false;
1950 if (aFlags & PathTransformFlags::UnexpandEnvVars) {
1951 if (!UnexpandEnvVars(aPath)) {
1952 return false;
1956 const WhitelistVec& whitelistedPaths = GetWhitelistedPaths();
1958 for (uint32_t i = 0; i < whitelistedPaths.length(); ++i) {
1959 const nsString& testPath = whitelistedPaths[i].first;
1960 const nsDependentString& substitution = whitelistedPaths[i].second;
1961 if (StringBeginsWith(aPath, testPath, nsCaseInsensitiveStringComparator)) {
1962 if (!substitution.IsVoid()) {
1963 aPath.Replace(0, testPath.Length(), substitution);
1965 return true;
1969 // For non-whitelisted paths, we strip the path component and just leave
1970 // the filename. We can't use nsLocalFile to do this because these paths may
1971 // begin with environment variables, and nsLocalFile doesn't like
1972 // non-absolute paths.
1973 const nsString& flatPath = PromiseFlatString(aPath);
1974 LPCWSTR leafStart = ::PathFindFileNameW(flatPath.get());
1975 ptrdiff_t cutLen = leafStart - flatPath.get();
1976 if (cutLen) {
1977 aPath.Cut(0, cutLen);
1978 } else if (aFlags & PathTransformFlags::RequireFilePath) {
1979 return false;
1982 return true;
1985 nsString WinUtils::GetPackageFamilyName() {
1986 nsString rv;
1988 UniquePtr<wchar_t[]> packageIdentity = mozilla::GetPackageFamilyName();
1989 if (packageIdentity) {
1990 rv = packageIdentity.get();
1993 return rv;
1996 bool WinUtils::GetClassName(HWND aHwnd, nsAString& aClassName) {
1997 const int bufferLength = 256;
1998 aClassName.SetLength(bufferLength);
2000 int length = ::GetClassNameW(aHwnd, (char16ptr_t)aClassName.BeginWriting(),
2001 bufferLength);
2002 if (length == 0) {
2003 return false;
2005 MOZ_RELEASE_ASSERT(length <= (bufferLength - 1));
2006 aClassName.Truncate(length);
2007 return true;
2010 static BOOL CALLBACK EnumUpdateWindowOcclusionProc(HWND aHwnd, LPARAM aLParam) {
2011 const bool* const enable = reinterpret_cast<bool*>(aLParam);
2012 nsWindow* window = WinUtils::GetNSWindowPtr(aHwnd);
2013 if (window) {
2014 window->MaybeEnableWindowOcclusion(*enable);
2016 return TRUE;
2019 void WinUtils::EnableWindowOcclusion(const bool aEnable) {
2020 if (aEnable) {
2021 WinWindowOcclusionTracker::Ensure();
2023 ::EnumWindows(EnumUpdateWindowOcclusionProc,
2024 reinterpret_cast<LPARAM>(&aEnable));
2027 bool WinUtils::GetTimezoneName(wchar_t* aBuffer) {
2028 DYNAMIC_TIME_ZONE_INFORMATION tzInfo;
2029 DWORD tzid = GetDynamicTimeZoneInformation(&tzInfo);
2031 if (tzid == TIME_ZONE_ID_INVALID) {
2032 return false;
2035 wcscpy_s(aBuffer, 128, tzInfo.TimeZoneKeyName);
2037 return true;
2040 bool WinUtils::MicaEnabled() {
2041 static bool sEnabled =
2042 IsWin1122H2OrLater() && StaticPrefs::widget_windows_mica_AtStartup();
2043 return sEnabled;
2046 // There are undocumented APIs to query/change the system DPI settings found by
2047 // https://github.com/lihas/ . We use those APIs only for testing purpose, i.e.
2048 // in mochitests or some such. To avoid exposing them in our official release
2049 // builds unexpectedly we restrict them only in debug builds.
2050 #ifdef DEBUG
2052 # define DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE (int)-4
2053 # define DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE (int)-3
2055 // Following two struts are copied from
2056 // https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.h
2059 * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
2060 * @brief used to fetch min, max, suggested, and currently applied DPI scaling
2061 * values. All values are relative to the recommended DPI scaling value Note
2062 * that DPI scaling is a property of the source, and not of target.
2064 struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET {
2065 DISPLAYCONFIG_DEVICE_INFO_HEADER header;
2067 * @brief min value of DPI scaling is always 100, minScaleRel gives no. of
2068 * steps down from recommended scaling eg. if minScaleRel is -3 => 100 is 3
2069 * steps down from recommended scaling => recommended scaling is 175%
2071 int32_t minScaleRel;
2074 * @brief currently applied DPI scaling value wrt the recommended value. eg.
2075 * if recommended value is 175%,
2076 * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1,
2077 * then current scale is 150%
2079 int32_t curScaleRel;
2082 * @brief maximum supported DPI scaling wrt recommended value
2084 int32_t maxScaleRel;
2088 * struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
2089 * @brief set DPI scaling value of a source
2090 * Note that DPI scaling is a property of the source, and not of target.
2092 struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET {
2093 DISPLAYCONFIG_DEVICE_INFO_HEADER header;
2095 * @brief The value we want to set. The value should be relative to the
2096 * recommended DPI scaling value of source. eg. if scaleRel == 1, and
2097 * recommended value is 175% => we are trying to set 200% scaling for the
2098 * source
2100 int32_t scaleRel;
2103 static int32_t sCurRelativeScaleStep = std::numeric_limits<int32_t>::max();
2105 static LONG SetRelativeScaleStep(LUID aAdapterId, int32_t aRelativeScaleStep) {
2106 DISPLAYCONFIG_SOURCE_DPI_SCALE_SET setDPIScale = {};
2107 setDPIScale.header.adapterId = aAdapterId;
2108 setDPIScale.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)
2109 DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE;
2110 setDPIScale.header.size = sizeof(setDPIScale);
2111 setDPIScale.scaleRel = aRelativeScaleStep;
2113 return DisplayConfigSetDeviceInfo(&setDPIScale.header);
2116 nsresult WinUtils::SetHiDPIMode(bool aHiDPI) {
2117 auto config = GetDisplayConfig();
2118 if (!config) {
2119 return NS_ERROR_NOT_AVAILABLE;
2122 if (config->mPaths.empty()) {
2123 return NS_ERROR_NOT_AVAILABLE;
2126 DISPLAYCONFIG_SOURCE_DPI_SCALE_GET dpiScale = {};
2127 dpiScale.header.adapterId = config->mPaths[0].targetInfo.adapterId;
2128 dpiScale.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)
2129 DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE;
2130 dpiScale.header.size = sizeof(dpiScale);
2131 LONG result = ::DisplayConfigGetDeviceInfo(&dpiScale.header);
2132 if (result != ERROR_SUCCESS) {
2133 return NS_ERROR_FAILURE;
2136 if (dpiScale.minScaleRel == dpiScale.maxScaleRel) {
2137 // We can't change the setting at all.
2138 return NS_ERROR_NOT_AVAILABLE;
2141 if (aHiDPI && dpiScale.curScaleRel == dpiScale.maxScaleRel) {
2142 // We've already at the maximum level.
2143 if (sCurRelativeScaleStep == std::numeric_limits<int32_t>::max()) {
2144 sCurRelativeScaleStep = dpiScale.curScaleRel;
2146 return NS_OK;
2149 if (!aHiDPI && dpiScale.curScaleRel == dpiScale.minScaleRel) {
2150 // We've already at the minimum level.
2151 if (sCurRelativeScaleStep == std::numeric_limits<int32_t>::max()) {
2152 sCurRelativeScaleStep = dpiScale.curScaleRel;
2154 return NS_OK;
2157 result = SetRelativeScaleStep(
2158 config->mPaths[0].targetInfo.adapterId,
2159 aHiDPI ? dpiScale.maxScaleRel : dpiScale.minScaleRel);
2160 if (result != ERROR_SUCCESS) {
2161 return NS_ERROR_FAILURE;
2164 if (sCurRelativeScaleStep == std::numeric_limits<int>::max()) {
2165 sCurRelativeScaleStep = dpiScale.curScaleRel;
2168 return NS_OK;
2171 nsresult WinUtils::RestoreHiDPIMode() {
2172 if (sCurRelativeScaleStep == std::numeric_limits<int>::max()) {
2173 // The DPI setting hasn't been changed.
2174 return NS_ERROR_UNEXPECTED;
2177 auto config = GetDisplayConfig();
2178 if (!config) {
2179 return NS_ERROR_NOT_AVAILABLE;
2182 if (config->mPaths.empty()) {
2183 return NS_ERROR_NOT_AVAILABLE;
2186 LONG result = SetRelativeScaleStep(config->mPaths[0].targetInfo.adapterId,
2187 sCurRelativeScaleStep);
2188 sCurRelativeScaleStep = std::numeric_limits<int32_t>::max();
2189 if (result != ERROR_SUCCESS) {
2190 return NS_ERROR_FAILURE;
2192 return NS_OK;
2194 #endif
2196 /* static */
2197 const char* WinUtils::WinEventToEventName(UINT msg) {
2198 const auto eventMsgInfo = mozilla::widget::gAllEvents.find(msg);
2199 return eventMsgInfo != mozilla::widget::gAllEvents.end()
2200 ? eventMsgInfo->second.mStr
2201 : nullptr;
2204 nsresult WinUtils::GetProcessImageName(DWORD aProcessId, nsAString& aName) {
2205 nsAutoHandle procHandle(
2206 ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, aProcessId));
2207 if (!procHandle) {
2208 return NS_ERROR_NOT_AVAILABLE;
2211 wchar_t path[MAX_PATH] = {L'\0'};
2212 auto len = ::GetProcessImageFileNameW(procHandle, path, std::size(path));
2213 if (!len) {
2214 return NS_ERROR_FAILURE;
2217 aName = path;
2218 return NS_OK;
2221 // Note to testers and/or test-authors: on Windows 10, and possibly on other
2222 // versions as well, supplying the `WS_EX_LAYOUTRTL` flag here has no effect
2223 // whatsoever on child common-dialogs **unless the system UI locale is also set
2224 // to an RTL language**.
2226 // If it is, the flag is still required; otherwise, the picker dialog will be
2227 // presented in English (or possibly some other LTR language) as a fallback.
2228 ScopedRtlShimWindow::ScopedRtlShimWindow(nsIWidget* aParent) : mWnd(nullptr) {
2229 NS_ENSURE_TRUE_VOID(aParent);
2231 // Headless windows don't have HWNDs, but also probably shouldn't be launching
2232 // print dialogs.
2233 HWND const hwnd = (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW);
2234 NS_ENSURE_TRUE_VOID(hwnd);
2236 nsWindow* const win = WinUtils::GetNSWindowPtr(hwnd);
2237 NS_ENSURE_TRUE_VOID(win);
2239 ATOM const wclass = ::GetClassWord(hwnd, GCW_ATOM);
2240 mWnd = ::CreateWindowExW(
2241 win->IsRTL() ? WS_EX_LAYOUTRTL : 0, (LPCWSTR)(uintptr_t)wclass, L"",
2242 WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
2243 hwnd, nullptr, nsToolkit::mDllInstance, nullptr);
2245 MOZ_ASSERT(mWnd);
2248 ScopedRtlShimWindow::~ScopedRtlShimWindow() {
2249 if (mWnd) {
2250 ::DestroyWindow(mWnd);
2254 } // namespace widget
2255 } // namespace mozilla