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/. */
9 #include <knownfolders.h>
12 #include "gfxPlatform.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"
40 #include "nsDirectoryServiceUtils.h"
41 #include "imgIContainer.h"
42 #include "imgITools.h"
43 #include "nsNetUtil.h"
44 #include "nsIOutputStream.h"
48 # include "nsIFaviconService.h"
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"
60 #include "TSFTextStore.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
;
76 NS_IMPL_ISUPPORTS(myDownloadObserver
, nsIDownloadObserver
)
77 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady
, nsIFaviconDataCallback
)
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
;
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");
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");
127 (GetDpiForWindowProc
)::GetProcAddress(user32Dll
, "GetDpiForWindow");
131 if (IsWin8OrLater()) {
132 sHasPackageIdentity
= mozilla::HasPackageIdentity();
137 LRESULT WINAPI
WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd
, UINT msg
,
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
);
152 void WinUtils::LogW(const wchar_t* fmt
, ...) {
153 va_list args
= nullptr;
154 if (!lstrlenW(fmt
)) {
158 int buflen
= _vscwprintf(fmt
, args
);
159 wchar_t* buffer
= new wchar_t[buflen
+ 1];
164 vswprintf(buffer
, buflen
, fmt
, args
);
167 // MSVC, including remote debug sessions
168 OutputDebugStringW(buffer
);
169 OutputDebugStringW(L
"\n");
172 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, nullptr, 0, nullptr, nullptr);
174 char* utf8
= new char[len
];
175 if (WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, utf8
, len
, nullptr,
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
));
190 void WinUtils::Log(const char* fmt
, ...) {
191 va_list args
= nullptr;
196 int buflen
= _vscprintf(fmt
, args
);
197 char* buffer
= new char[buflen
+ 1];
202 vsprintf(buffer
, fmt
, args
);
205 // MSVC, including remote debug sessions
206 OutputDebugStringA(buffer
);
207 OutputDebugStringW(L
"\n");
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
));
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;
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
235 return dpi
> 0 ? dpi
: 96;
239 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
243 MDT_EFFECTIVE_DPI
= 0,
246 MDT_DEFAULT
= MDT_EFFECTIVE_DPI
250 PROCESS_DPI_UNAWARE
= 0,
251 PROCESS_SYSTEM_DPI_AWARE
= 1,
252 PROCESS_PER_MONITOR_DPI_AWARE
= 2
253 } PROCESS_DPI_AWARENESS
;
256 typedef HRESULT(WINAPI
* GETDPIFORMONITORPROC
)(HMONITOR
, MONITOR_DPI_TYPE
, 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
);
270 (GETDPIFORMONITORPROC
)GetProcAddress(shcore
, "GetDpiForMonitor");
271 sGetProcessDpiAwareness
= (GETPROCESSDPIAWARENESSPROC
)GetProcAddress(
272 shcore
, "GetProcessDpiAwareness");
274 PROCESS_DPI_AWARENESS dpiAwareness
;
275 return sGetDpiForMonitor
&& sGetProcessDpiAwareness
&&
277 sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness
)) &&
278 dpiAwareness
== PROCESS_PER_MONITOR_DPI_AWARE
;
282 bool WinUtils::IsPerMonitorDPIAware() {
283 static bool perMonitorDPIAware
= SlowIsPerMonitorDPIAware();
284 return perMonitorDPIAware
;
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
);
296 // We're not per-monitor aware, use system DPI instead.
301 double WinUtils::LogToPhysFactor(HMONITOR aMonitor
) {
302 return MonitorDPI(aMonitor
) / 96.0;
306 int32_t WinUtils::LogToPhys(HMONITOR aMonitor
, double aValue
) {
307 return int32_t(NS_round(aValue
* LogToPhysFactor(aMonitor
)));
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
);
321 return static_cast<double>(dpi
) / 96.0;
324 return LogToPhysFactor(::MonitorFromWindow(ancestor
? ancestor
: aWnd
,
325 MONITOR_DEFAULTTOPRIMARY
));
330 WinUtils::GetPrimaryMonitor() {
331 const POINT pt
= {0, 0};
332 return ::MonitorFromPoint(pt
, MONITOR_DEFAULTTOPRIMARY
);
337 WinUtils::MonitorFromRect(const gfx::Rect
& rect
) {
338 // convert coordinates from desktop to device pixels for MonitorFromRect
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
);
351 bool WinUtils::HasSystemMetricsForDpi() {
352 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
353 return (sGetSystemMetricsForDpi
!= NULL
);
357 int WinUtils::GetSystemMetricsForDpi(int nIndex
, UINT dpi
) {
358 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
359 if (HasSystemMetricsForDpi()) {
360 return sGetSystemMetricsForDpi(nIndex
, dpi
);
362 double scale
= IsPerMonitorDPIAware() ? dpi
/ SystemDPI() : 1.0;
363 return NSToIntRound(::GetSystemMetrics(nIndex
) * scale
);
368 gfx::MarginDouble
WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC 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
,
403 a11y::LocalAccessible
* WinUtils::GetRootAccessibleForHWND(HWND aHwnd
) {
404 nsWindow
* window
= GetNSWindowPtr(aHwnd
);
409 return window
->GetAccessible();
411 #endif // ACCESSIBILITY
414 bool WinUtils::PeekMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
415 UINT aLastMessage
, UINT aOption
) {
416 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
419 HRESULT hr
= msgPump
->PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
,
421 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
424 return ::PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, aOption
);
428 bool WinUtils::GetMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
430 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
434 msgPump
->GetMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, &ret
);
435 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
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
;
452 void WinUtils::WaitForMessage(DWORD aTimeoutMs
) {
453 #if defined(ACCESSIBILITY)
454 static const DWORD waitFlags
= GetWaitFlags();
456 const DWORD waitFlags
= MWMO_INPUTAVAILABLE
;
459 const DWORD waitStart
= ::GetTickCount();
462 if (aTimeoutMs
!= INFINITE
) {
463 elapsed
= ::GetTickCount() - waitStart
;
465 if (elapsed
>= aTimeoutMs
) {
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
) {
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();
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;
499 if (haveSentMessagesPending
||
500 ::PeekMessageW(&msg
, nullptr, 0, 0, PM_NOREMOVE
)) {
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.
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");
519 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_32KEY
, &key
);
520 if (result
!= ERROR_SUCCESS
) {
522 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
);
523 if (result
!= ERROR_SUCCESS
) {
529 result
= ::RegQueryValueExW(key
, aValueName
, nullptr, &type
, (BYTE
*)aBuffer
,
532 if (result
!= ERROR_SUCCESS
|| (type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
)) {
536 aBuffer
[aBufferLength
/ sizeof(*aBuffer
) - 1] = 0;
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");
547 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_32KEY
, &key
);
548 if (result
!= ERROR_SUCCESS
) {
550 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
);
551 if (result
!= ERROR_SUCCESS
) {
560 HWND
WinUtils::GetTopLevelHWND(HWND aWnd
, bool aStopIfNotChild
,
561 bool aStopIfNotPopup
) {
563 HWND topWnd
= nullptr;
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
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
);
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
;
595 void WinUtils::SetNSWindowPtr(HWND aWnd
, nsWindow
* aWindow
) {
596 MOZ_ASSERT(NS_IsMainThread());
598 sExtantNSWindows
.Remove(aWnd
);
600 sExtantNSWindows
.InsertOrUpdate(aWnd
, aWindow
);
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
)++;
616 int32_t WinUtils::GetMonitorCount() {
617 int32_t monitorCount
= 0;
618 EnumDisplayMonitors(nullptr, nullptr, AddMonitor
, (LPARAM
)&monitorCount
);
623 bool WinUtils::IsOurProcessWindow(HWND aWnd
) {
628 ::GetWindowThreadProcessId(aWnd
, &processId
);
629 return (processId
== ::GetCurrentProcessId());
633 HWND
WinUtils::FindOurProcessWindow(HWND aWnd
) {
634 for (HWND wnd
= ::GetParent(aWnd
); wnd
; wnd
= ::GetParent(wnd
)) {
635 if (IsOurProcessWindow(wnd
)) {
642 static bool IsPointInWindow(HWND aWnd
, const POINT
& aPointInScreen
) {
644 if (!::GetWindowRect(aWnd
, &bounds
)) {
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
)) {
662 HWND childWnd
= ::GetTopWindow(aWnd
);
664 HWND topmostWnd
= FindTopmostWindowAtPoint(childWnd
, aPointInScreen
);
668 childWnd
= ::GetNextWindow(childWnd
, GW_HWNDNEXT
);
674 struct FindOurWindowAtPointInfo
{
675 POINT mInPointInScreen
;
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.
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
);
693 // This window doesn't contain the point; continue enumerating.
697 // Return the HWND and stop enumerating.
698 info
->mOutWnd
= childWnd
;
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
));
714 UINT
WinUtils::GetInternalMessage(UINT aNativeMessage
) {
715 switch (aNativeMessage
) {
717 return MOZ_WM_MOUSEVWHEEL
;
719 return MOZ_WM_MOUSEHWHEEL
;
721 return MOZ_WM_VSCROLL
;
723 return MOZ_WM_HSCROLL
;
725 return aNativeMessage
;
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
;
741 return aInternalMessage
;
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
);
758 uint16_t WinUtils::GetMousePointerID() {
759 LPARAM lParamExtraInfo
= ::GetMessageExtraInfo();
760 return lParamExtraInfo
& TABLET_INK_ID_MASK
;
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
);
774 MSG
WinUtils::InitMSG(UINT aMessage
, WPARAM wParam
, LPARAM lParam
, HWND aWnd
) {
776 msg
.message
= aMessage
;
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
791 ************************************************************************/
793 AsyncFaviconDataReady::AsyncFaviconDataReady(
794 nsIURI
* aNewURI
, RefPtr
<LazyIdleThread
>& aIOThread
, const bool aURLShortcut
,
795 already_AddRefed
<nsIRunnable
> aRunnable
)
797 mIOThread(aIOThread
),
798 mRunnable(aRunnable
),
799 mURLShortcut(aURLShortcut
) {}
802 myDownloadObserver::OnDownloadComplete(nsIDownloader
* downloader
,
803 nsIRequest
* request
, nsresult status
,
808 nsresult
AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
813 nsCOMPtr
<nsIFile
> icoFile
;
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");
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
);
841 AsyncFaviconDataReady::OnComplete(nsIURI
* aFaviconURI
, uint32_t aDataLen
,
842 const uint8_t* aData
,
843 const nsACString
& aMimeType
,
845 if (!aDataLen
|| !aData
) {
847 OnFaviconDataNotAvailable();
853 nsCOMPtr
<nsIFile
> icoFile
;
855 FaviconHelper::GetOutputIconPath(mNewURI
, icoFile
, mURLShortcut
);
856 NS_ENSURE_SUCCESS(rv
, rv
);
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
),
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
;
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);
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());
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())));
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();
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
928 UniquePtr
<uint8_t[]> data
= SurfaceToPackedBGRA(dataSurface
);
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
);
944 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
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
),
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");
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");
971 rv
= comFile
->InitWithPath(mIconPath
);
972 if (NS_SUCCEEDED(rv
)) {
973 nsCOMPtr
<nsIFile
> dirPath
;
974 comFile
->GetParent(getter_AddRefs(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");
980 rv
= NS_ERROR_FAILURE
;
990 nsresult rv
= gfxUtils::EncodeSourceSurface(surface
, ImageType::ICO
, u
""_ns
,
991 gfxUtils::eBinaryEncode
, file
);
993 NS_ENSURE_SUCCESS(rv
, rv
);
1001 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {}
1003 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
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
1031 nsCOMPtr
<nsIFile
> currFile
;
1032 if (NS_FAILED(entries
->GetNextFile(getter_AddRefs(currFile
))) || !currFile
)
1036 if (NS_FAILED(currFile
->GetPath(path
))) continue;
1038 if (StringTail(path
, 4).LowerCaseEqualsASCII(".ico")) {
1039 // Check if the cached ICO file 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
) {
1058 // We found an ICO file that exists, so we should remove it
1059 currFile
->Remove(false);
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
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
1093 rv
= icoFile
->Exists(&exists
);
1094 NS_ENSURE_SUCCESS(rv
, rv
);
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
;
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
);
1125 nsresult
FaviconHelper::HashURI(nsCOMPtr
<nsICryptoHash
>& aCryptoHash
,
1126 nsIURI
* aUri
, nsACString
& aUriHash
) {
1127 if (!aUri
) return NS_ERROR_INVALID_ARG
;
1130 nsresult rv
= aUri
->GetSpec(spec
);
1131 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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
) {
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
);
1172 rv
= aICOFile
->AppendNative(nsDependentCString(kJumpListCacheDir
));
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
);
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
;
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);
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
;
1221 const char PREF_ICOTIMEOUT
[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1222 icoReCacheSecondsTimeout
=
1223 Preferences::GetInt(PREF_ICOTIMEOUT
, kSecondsPerDay
);
1224 alreadyObtained
= true;
1225 return icoReCacheSecondsTimeout
;
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
);
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
));
1255 LayoutDeviceIntRect
WinUtils::ToIntRect(const RECT
& aRect
) {
1256 return LayoutDeviceIntRect(aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
1257 aRect
.bottom
- aRect
.top
);
1261 bool WinUtils::IsIMEEnabled(const InputContext
& aInputContext
) {
1262 return IsIMEEnabled(aInputContext
.mIMEState
.mEnabled
);
1266 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState
) {
1267 return aIMEState
== IMEEnabled::Enabled
;
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]));
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
));
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
);
1317 nsresult
WinUtils::WriteBitmap(nsIFile
* aFile
, SourceSurface
* surface
) {
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
1339 memset(&bmi
, 0, sizeof(BITMAPV4HEADER
));
1340 bmi
.bV4Size
= sizeof(BITMAPV4HEADER
);
1341 bmi
.bV4Width
= width
;
1342 bmi
.bV4Height
= height
;
1344 bmi
.bV4BitCount
= (WORD
)bytesPerPixel
* 8;
1345 bmi
.bV4V4Compression
= hasAlpha
? BI_BITFIELDS
: BI_RGB
;
1346 bmi
.bV4SizeImage
= bytesPerRow
* height
;
1347 bmi
.bV4CSType
= LCS_sRGB
;
1349 bmi
.bV4RedMask
= 0x00FF0000;
1350 bmi
.bV4GreenMask
= 0x0000FF00;
1351 bmi
.bV4BlueMask
= 0x000000FF;
1352 bmi
.bV4AlphaMask
= 0xFF000000;
1355 BITMAPFILEHEADER bf
;
1357 bf
.bfType
= 0x4D42; // 'BM'
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
;
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
)) {
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
;
1396 stream
->Write(((const char*)map
.mData
) + i
, bytesPerRow
, &written
);
1397 if (written
== bytesPerRow
) {
1400 rv
= NS_ERROR_FAILURE
;
1411 dataSurface
->Unmap();
1416 // This is in use here and in dom/events/TouchEvent.cpp
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
);
1428 uint32_t WinUtils::GetMaxTouchPoints() {
1429 if (IsTouchDeviceSupportPresent()) {
1430 return GetSystemMetrics(SM_MAXIMUMTOUCHES
);
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
);
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
);
1465 static bool IsTabletDevice() {
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()) {
1475 if (WindowsUIUtils::GetInTabletMode()) {
1479 if (!GetSystemMetrics(SM_MAXIMUMTOUCHES
)) {
1483 // If the device is docked, the user is treating the device as a PC.
1484 if (GetSystemMetrics(SM_SYSTEMDOCKED
)) {
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
))) {
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
);
1505 static bool IsMousePresent() {
1506 if (!::GetSystemMetrics(SM_MOUSEPRESENT
)) {
1510 DWORD count
= InputDeviceUtils::CountMouseDevices();
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.
1520 (WinUtils::IsTouchDeviceSupportPresent() || IsTabletDevice())) {
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
;
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
;
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",
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());
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) {
1588 } else if (aPath
.compare(0, 4, L
"\\\\?\\") == 0) {
1592 LOG_D("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
1598 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile
* aPath
) {
1601 nsAutoString filePath
;
1602 nsresult rv
= aPath
->GetPath(filePath
);
1603 if (NS_WARN_IF(NS_FAILED(rv
))) {
1607 std::wstring
resolvedPath(filePath
.get());
1608 if (!ResolveJunctionPointsAndSymLinks(resolvedPath
)) {
1612 rv
= aPath
->InitWithPath(nsDependentString(resolvedPath
.c_str()));
1613 if (NS_WARN_IF(NS_FAILED(rv
))) {
1621 bool WinUtils::RunningFromANetworkDrive() {
1622 wchar_t exePath
[MAX_PATH
];
1623 if (!::GetModuleFileNameW(nullptr, exePath
, MAX_PATH
)) {
1627 std::wstring
exeString(exePath
);
1628 if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString
)) {
1632 wchar_t volPath
[MAX_PATH
];
1633 if (!::GetVolumePathNameW(exeString
.c_str(), volPath
, MAX_PATH
)) {
1637 return (::GetDriveTypeW(volPath
) == DRIVE_REMOTE
);
1641 bool WinUtils::CanonicalizePath(nsAString
& aPath
) {
1642 wchar_t tempPath
[MAX_PATH
+ 1];
1643 if (!PathCanonicalizeW(tempPath
,
1644 (char16ptr_t
)PromiseFlatString(aPath
).get())) {
1648 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
1653 bool WinUtils::MakeLongPath(nsAString
& aPath
) {
1654 wchar_t tempPath
[MAX_PATH
+ 1];
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.
1661 } else if (longResult
) {
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.
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
))) {
1679 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
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
);
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
);
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
1730 * @see PreparePathForTelemetry for an example of its usage.
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()) {
1743 SchedulerGroup::Dispatch(
1744 TaskCategory::Other
,
1745 NS_NewRunnableFunction("WinUtils::GetWhitelistedPaths",
1746 std::move(setClearFn
)));
1749 return BuildWhitelist();
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
1760 bool WinUtils::GetAppInitDLLs(nsAString
& aOutput
) {
1763 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1764 L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
1765 0, KEY_QUERY_VALUE
, &hkey
)) {
1768 nsAutoRegKey
key(hkey
);
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
) {
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.
1784 const wchar_t kAppInitDLLs
[] = L
"AppInit_DLLs";
1785 // Query for required buffer size
1786 status
= RegQueryValueExW(hkey
, kAppInitDLLs
, nullptr, nullptr, nullptr,
1788 if (status
!= ERROR_SUCCESS
) {
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
) {
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
);
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()) {
1813 aOutput
+= cleanPath
;
1815 token
= wcstok_s(nullptr, kDelimiters
, &tokenContext
);
1821 bool WinUtils::PreparePathForTelemetry(nsAString
& aPath
,
1822 PathTransformFlags aFlags
) {
1823 if (aFlags
& PathTransformFlags::Canonicalize
) {
1824 if (!CanonicalizePath(aPath
)) {
1828 if (aFlags
& PathTransformFlags::Lengthen
) {
1829 if (!MakeLongPath(aPath
)) {
1833 if (aFlags
& PathTransformFlags::UnexpandEnvVars
) {
1834 if (!UnexpandEnvVars(aPath
)) {
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
);
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();
1860 aPath
.Cut(0, cutLen
);
1861 } else if (aFlags
& PathTransformFlags::RequireFilePath
) {
1868 nsString
WinUtils::GetPackageFamilyName() {
1871 UniquePtr
<wchar_t[]> packageIdentity
= mozilla::GetPackageFamilyName();
1872 if (packageIdentity
) {
1873 rv
= packageIdentity
.get();
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(),
1888 MOZ_RELEASE_ASSERT(length
<= (bufferLength
- 1));
1889 aClassName
.Truncate(length
);
1893 static BOOL CALLBACK
EnumUpdateWindowOcclusionProc(HWND aHwnd
, LPARAM aLParam
) {
1894 const bool* const enable
= reinterpret_cast<bool*>(aLParam
);
1895 nsWindow
* window
= WinUtils::GetNSWindowPtr(aHwnd
);
1897 window
->MaybeEnableWindowOcclusion(*enable
);
1902 void WinUtils::EnableWindowOcclusion(const bool 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
) {
1918 wcscpy_s(aBuffer
, 128, tzInfo
.TimeZoneKeyName
);
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.
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
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();
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
;
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
;
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
;
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();
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
;
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
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
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);
2116 ScopedRtlShimWindow::~ScopedRtlShimWindow() {
2118 ::DestroyWindow(mWnd
);
2122 } // namespace widget
2123 } // namespace mozilla