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>
13 #include "gfxPlatform.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"
41 #include "nsDirectoryServiceUtils.h"
42 #include "imgIContainer.h"
43 #include "imgITools.h"
44 #include "nsNetUtil.h"
45 #include "nsIOutputStream.h"
49 # include "nsIFaviconService.h"
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"
61 #include "TSFTextStore.h"
63 #include <shellscalingapi.h>
67 mozilla::LazyLogModule
gWindowsLog("Widget");
69 using namespace mozilla::gfx
;
75 NS_IMPL_ISUPPORTS(myDownloadObserver
, nsIDownloadObserver
)
76 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady
, nsIFaviconDataCallback
)
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
;
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");
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");
126 (GetDpiForWindowProc
)::GetProcAddress(user32Dll
, "GetDpiForWindow");
130 sHasPackageIdentity
= mozilla::HasPackageIdentity();
134 LRESULT WINAPI
WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd
, UINT msg
,
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
);
149 void WinUtils::LogW(const wchar_t* fmt
, ...) {
150 va_list args
= nullptr;
151 if (!lstrlenW(fmt
)) {
155 int buflen
= _vscwprintf(fmt
, args
);
156 wchar_t* buffer
= new wchar_t[buflen
+ 1];
161 vswprintf(buffer
, buflen
, fmt
, args
);
164 // MSVC, including remote debug sessions
165 OutputDebugStringW(buffer
);
166 OutputDebugStringW(L
"\n");
169 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, nullptr, 0, nullptr, nullptr);
171 char* utf8
= new char[len
];
172 if (WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, utf8
, len
, nullptr,
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
));
187 void WinUtils::Log(const char* fmt
, ...) {
188 va_list args
= nullptr;
193 int buflen
= _vscprintf(fmt
, args
);
194 char* buffer
= new char[buflen
+ 1];
199 vsprintf(buffer
, fmt
, args
);
202 // MSVC, including remote debug sessions
203 OutputDebugStringA(buffer
);
204 OutputDebugStringW(L
"\n");
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
));
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;
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
232 return dpi
> 0 ? dpi
: 96;
236 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
238 typedef HRESULT(WINAPI
* GETDPIFORMONITORPROC
)(HMONITOR
, MONITOR_DPI_TYPE
, 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
);
252 (GETDPIFORMONITORPROC
)GetProcAddress(shcore
, "GetDpiForMonitor");
253 sGetProcessDpiAwareness
= (GETPROCESSDPIAWARENESSPROC
)GetProcAddress(
254 shcore
, "GetProcessDpiAwareness");
256 PROCESS_DPI_AWARENESS dpiAwareness
;
257 return sGetDpiForMonitor
&& sGetProcessDpiAwareness
&&
259 sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness
)) &&
260 dpiAwareness
== PROCESS_PER_MONITOR_DPI_AWARE
;
264 bool WinUtils::IsPerMonitorDPIAware() {
265 static bool perMonitorDPIAware
= SlowIsPerMonitorDPIAware();
266 return perMonitorDPIAware
;
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
);
278 // We're not per-monitor aware, use system DPI instead.
283 double WinUtils::LogToPhysFactor(HMONITOR aMonitor
) {
284 return MonitorDPI(aMonitor
) / 96.0;
288 int32_t WinUtils::LogToPhys(HMONITOR aMonitor
, double aValue
) {
289 return int32_t(NS_round(aValue
* LogToPhysFactor(aMonitor
)));
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
);
303 return static_cast<double>(dpi
) / 96.0;
306 return LogToPhysFactor(::MonitorFromWindow(ancestor
? ancestor
: aWnd
,
307 MONITOR_DEFAULTTOPRIMARY
));
312 WinUtils::GetPrimaryMonitor() {
313 const POINT pt
= {0, 0};
314 return ::MonitorFromPoint(pt
, MONITOR_DEFAULTTOPRIMARY
);
319 WinUtils::MonitorFromRect(const gfx::Rect
& rect
) {
320 // convert coordinates from desktop to device pixels for MonitorFromRect
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
);
333 bool WinUtils::HasSystemMetricsForDpi() {
334 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
335 return (sGetSystemMetricsForDpi
!= NULL
);
339 int WinUtils::GetSystemMetricsForDpi(int nIndex
, UINT dpi
) {
340 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
341 if (HasSystemMetricsForDpi()) {
342 return sGetSystemMetricsForDpi(nIndex
, dpi
);
344 double scale
= IsPerMonitorDPIAware() ? dpi
/ SystemDPI() : 1.0;
345 return NSToIntRound(::GetSystemMetrics(nIndex
) * scale
);
350 gfx::MarginDouble
WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC 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
,
385 a11y::LocalAccessible
* WinUtils::GetRootAccessibleForHWND(HWND aHwnd
) {
386 nsWindow
* window
= GetNSWindowPtr(aHwnd
);
391 return window
->GetAccessible();
393 #endif // ACCESSIBILITY
396 bool WinUtils::PeekMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
397 UINT aLastMessage
, UINT aOption
) {
398 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
401 HRESULT hr
= msgPump
->PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
,
403 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
406 return ::PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, aOption
);
410 bool WinUtils::GetMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
412 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
416 msgPump
->GetMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, &ret
);
417 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
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
;
434 void WinUtils::WaitForMessage(DWORD aTimeoutMs
) {
435 #if defined(ACCESSIBILITY)
436 static const DWORD waitFlags
= GetWaitFlags();
438 const DWORD waitFlags
= MWMO_INPUTAVAILABLE
;
441 const DWORD waitStart
= ::GetTickCount();
444 if (aTimeoutMs
!= INFINITE
) {
445 elapsed
= ::GetTickCount() - waitStart
;
447 if (elapsed
>= aTimeoutMs
) {
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
) {
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();
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;
481 if (haveSentMessagesPending
||
482 ::PeekMessageW(&msg
, nullptr, 0, 0, PM_NOREMOVE
)) {
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.
494 HWND
WinUtils::GetTopLevelHWND(HWND aWnd
, bool aStopIfNotChild
,
495 bool aStopIfNotPopup
) {
497 HWND topWnd
= nullptr;
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
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
);
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
;
529 void WinUtils::SetNSWindowPtr(HWND aWnd
, nsWindow
* aWindow
) {
530 MOZ_ASSERT(NS_IsMainThread());
532 sExtantNSWindows
.Remove(aWnd
);
534 sExtantNSWindows
.InsertOrUpdate(aWnd
, aWindow
);
539 nsWindow
* WinUtils::GetNSWindowPtr(HWND aWnd
) {
540 MOZ_ASSERT(NS_IsMainThread());
541 return sExtantNSWindows
.Get(aWnd
); // or nullptr
545 bool WinUtils::IsOurProcessWindow(HWND aWnd
) {
550 ::GetWindowThreadProcessId(aWnd
, &processId
);
551 return (processId
== ::GetCurrentProcessId());
555 HWND
WinUtils::FindOurProcessWindow(HWND aWnd
) {
556 for (HWND wnd
= ::GetParent(aWnd
); wnd
; wnd
= ::GetParent(wnd
)) {
557 if (IsOurProcessWindow(wnd
)) {
564 static bool IsPointInWindow(HWND aWnd
, const POINT
& aPointInScreen
) {
566 if (!::GetWindowRect(aWnd
, &bounds
)) {
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
)) {
584 HWND childWnd
= ::GetTopWindow(aWnd
);
586 HWND topmostWnd
= FindTopmostWindowAtPoint(childWnd
, aPointInScreen
);
590 childWnd
= ::GetNextWindow(childWnd
, GW_HWNDNEXT
);
596 struct FindOurWindowAtPointInfo
{
597 POINT mInPointInScreen
;
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.
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
);
615 // This window doesn't contain the point; continue enumerating.
619 // Return the HWND and stop enumerating.
620 info
->mOutWnd
= childWnd
;
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
));
636 UINT
WinUtils::GetInternalMessage(UINT aNativeMessage
) {
637 switch (aNativeMessage
) {
639 return MOZ_WM_MOUSEVWHEEL
;
641 return MOZ_WM_MOUSEHWHEEL
;
643 return MOZ_WM_VSCROLL
;
645 return MOZ_WM_HSCROLL
;
647 return aNativeMessage
;
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
;
663 return aInternalMessage
;
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
);
680 uint16_t WinUtils::GetMousePointerID() {
681 LPARAM lParamExtraInfo
= ::GetMessageExtraInfo();
682 return lParamExtraInfo
& TABLET_INK_ID_MASK
;
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
);
696 MSG
WinUtils::InitMSG(UINT aMessage
, WPARAM wParam
, LPARAM lParam
, HWND aWnd
) {
698 msg
.message
= aMessage
;
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
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
718 ************************************************************************/
720 AsyncFaviconDataReady::AsyncFaviconDataReady(
721 nsIURI
* aNewURI
, RefPtr
<nsISerialEventTarget
> aIOThread
,
722 const bool aURLShortcut
, already_AddRefed
<nsIRunnable
> aRunnable
,
723 UniquePtr
<MozPromiseHolder
<ObtainCachedIconFileAsyncPromise
>>
726 mIOThread(aIOThread
),
727 mRunnable(aRunnable
),
728 mPromiseHolder(std::move(aPromiseHolder
)),
729 mURLShortcut(aURLShortcut
) {}
732 myDownloadObserver::OnDownloadComplete(nsIDownloader
* downloader
,
733 nsIRequest
* request
, nsresult status
,
738 nsresult
AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
743 nsCOMPtr
<nsIFile
> icoFile
;
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");
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
);
771 AsyncFaviconDataReady::OnComplete(nsIURI
* aFaviconURI
, uint32_t aDataLen
,
772 const uint8_t* aData
,
773 const nsACString
& aMimeType
,
775 if (!aDataLen
|| !aData
) {
777 OnFaviconDataNotAvailable();
783 nsCOMPtr
<nsIFile
> icoFile
;
785 FaviconHelper::GetOutputIconPath(mNewURI
, icoFile
, mURLShortcut
);
786 NS_ENSURE_SUCCESS(rv
, rv
);
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
),
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
;
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);
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());
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())));
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();
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
858 UniquePtr
<uint8_t[]> data
= SurfaceToPackedBGRA(dataSurface
);
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
);
874 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
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
>>
881 : mIconPath(aIconPath
),
882 mBuffer(std::move(aBuffer
)),
883 mRunnable(aRunnable
),
884 mPromiseHolder(std::move(aPromiseHolder
)),
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");
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
) {
909 file
= _wfopen(mIconPath
.get(), L
"wb");
911 return NS_ERROR_FAILURE
;
914 nsresult rv
= gfxUtils::EncodeSourceSurface(surface
, ImageType::ICO
, u
""_ns
,
915 gfxUtils::eBinaryEncode
, file
);
917 NS_ENSURE_SUCCESS(rv
, rv
);
922 if (mPromiseHolder
) {
923 mPromiseHolder
->ResolveIfExists(mIconPath
, __func__
);
928 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {
929 if (mPromiseHolder
) {
930 mPromiseHolder
->RejectIfExists(NS_ERROR_FAILURE
, __func__
);
934 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
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
962 nsCOMPtr
<nsIFile
> currFile
;
963 if (NS_FAILED(entries
->GetNextFile(getter_AddRefs(currFile
))) || !currFile
)
967 if (NS_FAILED(currFile
->GetPath(path
))) continue;
969 if (StringTail(path
, 4).LowerCaseEqualsASCII(".ico")) {
970 // Check if the cached ICO file exists
972 if (NS_FAILED(currFile
->Exists(&exists
)) || !exists
) continue;
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
) {
989 // We found an ICO file that exists, so we should remove it
990 currFile
->Remove(false);
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
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
1024 rv
= icoFile
->Exists(&exists
);
1025 NS_ENSURE_SUCCESS(rv
, rv
);
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(),
1042 return NS_ERROR_NOT_AVAILABLE
;
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
);
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.
1076 * The thread to perform the cache check and fetch/write on.
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
;
1095 GetOutputIconPath(aFaviconPageURI
, icoFile
, useShortcutCacheDir
);
1096 if (NS_FAILED(rv
)) {
1097 return ObtainCachedIconFileAsyncPromise::CreateAndReject(rv
, __func__
);
1100 int32_t icoReCacheSecondsTimeout
= GetICOCacheSecondsTimeout();
1103 aIOThread
, "FaviconHelper::ObtainCachedIconFileAsync disk cache check",
1104 [icoFile
= std::move(icoFile
), icoReCacheSecondsTimeout
,
1105 pageURI
= std::move(aFaviconPageURI
), useShortcutCacheDir
]() {
1106 MOZ_ASSERT(!NS_IsMainThread());
1108 nsresult rv
= icoFile
->Exists(&exists
);
1110 if (NS_FAILED(rv
)) {
1111 return ObtainCachedIconFileAsyncPromise::CreateAndReject(
1112 rv
, "ObtainCachedIconFileAsync disk cache check: exists failed");
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(
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();
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,
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
) {
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()),
1183 NS_ENSURE_SUCCESS(rv
, rv
);
1185 rv
= cryptoHash
->Finish(true, aUriHash
);
1186 NS_ENSURE_SUCCESS(rv
, rv
);
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
) {
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
);
1216 rv
= aICOFile
->AppendNative(nsDependentCString(kJumpListCacheDir
));
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
);
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
>>
1236 MOZ_ASSERT(NS_IsMainThread());
1237 nsCOMPtr
<nsIRunnable
> runnable
= aRunnable
;
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);
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
;
1269 const char PREF_ICOTIMEOUT
[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1270 icoReCacheSecondsTimeout
=
1271 Preferences::GetInt(PREF_ICOTIMEOUT
, kSecondsPerDay
);
1272 alreadyObtained
= true;
1273 return icoReCacheSecondsTimeout
;
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
);
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
));
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
;
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
) {
1334 .right
= aRect
.XMost(),
1335 .bottom
= aRect
.YMost(),
1340 bool WinUtils::IsIMEEnabled(const InputContext
& aInputContext
) {
1341 return IsIMEEnabled(aInputContext
.mIMEState
.mEnabled
);
1345 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState
) {
1346 return aIMEState
== IMEEnabled::Enabled
;
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]));
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
));
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
);
1396 nsresult
WinUtils::WriteBitmap(nsIFile
* aFile
, SourceSurface
* surface
) {
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
1418 memset(&bmi
, 0, sizeof(BITMAPV4HEADER
));
1419 bmi
.bV4Size
= sizeof(BITMAPV4HEADER
);
1420 bmi
.bV4Width
= width
;
1421 bmi
.bV4Height
= height
;
1423 bmi
.bV4BitCount
= (WORD
)bytesPerPixel
* 8;
1424 bmi
.bV4V4Compression
= hasAlpha
? BI_BITFIELDS
: BI_RGB
;
1425 bmi
.bV4SizeImage
= bytesPerRow
* height
;
1426 bmi
.bV4CSType
= LCS_sRGB
;
1428 bmi
.bV4RedMask
= 0x00FF0000;
1429 bmi
.bV4GreenMask
= 0x0000FF00;
1430 bmi
.bV4BlueMask
= 0x000000FF;
1431 bmi
.bV4AlphaMask
= 0xFF000000;
1434 BITMAPFILEHEADER bf
;
1436 bf
.bfType
= 0x4D42; // 'BM'
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
;
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
)) {
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
;
1475 stream
->Write(((const char*)map
.mData
) + i
, bytesPerRow
, &written
);
1476 if (written
== bytesPerRow
) {
1479 rv
= NS_ERROR_FAILURE
;
1490 dataSurface
->Unmap();
1495 // This is in use here and in dom/events/TouchEvent.cpp
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
);
1507 uint32_t WinUtils::GetMaxTouchPoints() {
1508 if (IsTouchDeviceSupportPresent()) {
1509 return GetSystemMetrics(SM_MAXIMUMTOUCHES
);
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
);
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
);
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
);
1553 // Standard clipboard formats
1554 // https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
1557 aOutput
.Append(u
"CF_TEXT"_ns
);
1559 case CF_BITMAP
: // 2
1560 aOutput
.Append(u
"CF_BITMAP"_ns
);
1563 aOutput
.Append(u
"CF_DIB"_ns
);
1565 case CF_UNICODETEXT
: // 13
1566 aOutput
.Append(u
"CF_UNICODETEXT"_ns
);
1568 case CF_HDROP
: // 15
1569 aOutput
.Append(u
"CF_HDROP"_ns
);
1571 case CF_DIBV5
: // 17
1572 aOutput
.Append(u
"CF_DIBV5"_ns
);
1575 aOutput
.AppendPrintf("%u", aFormat
);
1580 static bool IsTabletDevice() {
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()) {
1589 if (!GetSystemMetrics(SM_MAXIMUMTOUCHES
)) {
1593 // If the device is docked, the user is treating the device as a PC.
1594 if (GetSystemMetrics(SM_SYSTEMDOCKED
)) {
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
))) {
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
);
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
1622 return !!::GetSystemMetrics(SM_MOUSEPRESENT
);
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
);
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
;
1671 bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring
& aPath
) {
1672 static mozilla::LazyLogModule
sNTFSLog("NTFS");
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",
1691 DWORD pathLen
= GetFinalPathNameByHandleW(
1692 handle
, path
, MAX_PATH
, FILE_NAME_NORMALIZED
| VOLUME_NAME_DOS
);
1693 if (pathLen
== 0 || pathLen
>= MAX_PATH
) {
1695 sNTFSLog
, LogLevel::Error
,
1696 ("GetFinalPathNameByHandleW failed. GetLastError=%lu", GetLastError()));
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) {
1706 } else if (aPath
.compare(0, 4, L
"\\\\?\\") == 0) {
1710 MOZ_LOG(sNTFSLog
, LogLevel::Debug
,
1711 ("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
1717 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile
* aPath
) {
1720 nsAutoString filePath
;
1721 nsresult rv
= aPath
->GetPath(filePath
);
1722 if (NS_WARN_IF(NS_FAILED(rv
))) {
1726 std::wstring
resolvedPath(filePath
.get());
1727 if (!ResolveJunctionPointsAndSymLinks(resolvedPath
)) {
1731 rv
= aPath
->InitWithPath(nsDependentString(resolvedPath
.c_str()));
1732 if (NS_WARN_IF(NS_FAILED(rv
))) {
1740 bool WinUtils::RunningFromANetworkDrive() {
1741 wchar_t exePath
[MAX_PATH
];
1742 if (!::GetModuleFileNameW(nullptr, exePath
, MAX_PATH
)) {
1746 std::wstring
exeString(exePath
);
1747 if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString
)) {
1751 wchar_t volPath
[MAX_PATH
];
1752 if (!::GetVolumePathNameW(exeString
.c_str(), volPath
, MAX_PATH
)) {
1756 return (::GetDriveTypeW(volPath
) == DRIVE_REMOTE
);
1760 bool WinUtils::CanonicalizePath(nsAString
& aPath
) {
1761 wchar_t tempPath
[MAX_PATH
+ 1];
1762 if (!PathCanonicalizeW(tempPath
,
1763 (char16ptr_t
)PromiseFlatString(aPath
).get())) {
1767 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
1772 bool WinUtils::MakeLongPath(nsAString
& aPath
) {
1773 wchar_t tempPath
[MAX_PATH
+ 1];
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.
1780 } else if (longResult
) {
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.
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
))) {
1798 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
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
);
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
);
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
1849 * @see PreparePathForTelemetry for an example of its usage.
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()) {
1862 SchedulerGroup::Dispatch(NS_NewRunnableFunction(
1863 "WinUtils::GetWhitelistedPaths", std::move(setClearFn
)));
1866 return BuildWhitelist();
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
1877 bool WinUtils::GetAppInitDLLs(nsAString
& aOutput
) {
1880 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1881 L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
1882 0, KEY_QUERY_VALUE
, &hkey
)) {
1885 nsAutoRegKey
key(hkey
);
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
) {
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.
1901 const wchar_t kAppInitDLLs
[] = L
"AppInit_DLLs";
1902 // Query for required buffer size
1903 status
= RegQueryValueExW(hkey
, kAppInitDLLs
, nullptr, nullptr, nullptr,
1905 if (status
!= ERROR_SUCCESS
) {
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
) {
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
);
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()) {
1930 aOutput
+= cleanPath
;
1932 token
= wcstok_s(nullptr, kDelimiters
, &tokenContext
);
1938 bool WinUtils::PreparePathForTelemetry(nsAString
& aPath
,
1939 PathTransformFlags aFlags
) {
1940 if (aFlags
& PathTransformFlags::Canonicalize
) {
1941 if (!CanonicalizePath(aPath
)) {
1945 if (aFlags
& PathTransformFlags::Lengthen
) {
1946 if (!MakeLongPath(aPath
)) {
1950 if (aFlags
& PathTransformFlags::UnexpandEnvVars
) {
1951 if (!UnexpandEnvVars(aPath
)) {
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
);
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();
1977 aPath
.Cut(0, cutLen
);
1978 } else if (aFlags
& PathTransformFlags::RequireFilePath
) {
1985 nsString
WinUtils::GetPackageFamilyName() {
1988 UniquePtr
<wchar_t[]> packageIdentity
= mozilla::GetPackageFamilyName();
1989 if (packageIdentity
) {
1990 rv
= packageIdentity
.get();
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(),
2005 MOZ_RELEASE_ASSERT(length
<= (bufferLength
- 1));
2006 aClassName
.Truncate(length
);
2010 static BOOL CALLBACK
EnumUpdateWindowOcclusionProc(HWND aHwnd
, LPARAM aLParam
) {
2011 const bool* const enable
= reinterpret_cast<bool*>(aLParam
);
2012 nsWindow
* window
= WinUtils::GetNSWindowPtr(aHwnd
);
2014 window
->MaybeEnableWindowOcclusion(*enable
);
2019 void WinUtils::EnableWindowOcclusion(const bool 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
) {
2035 wcscpy_s(aBuffer
, 128, tzInfo
.TimeZoneKeyName
);
2040 bool WinUtils::MicaEnabled() {
2041 static bool sEnabled
=
2042 IsWin1122H2OrLater() && StaticPrefs::widget_windows_mica_AtStartup();
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.
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
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();
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
;
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
;
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
;
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();
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
;
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
2204 nsresult
WinUtils::GetProcessImageName(DWORD aProcessId
, nsAString
& aName
) {
2205 nsAutoHandle
procHandle(
2206 ::OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, aProcessId
));
2208 return NS_ERROR_NOT_AVAILABLE
;
2211 wchar_t path
[MAX_PATH
] = {L
'\0'};
2212 auto len
= ::GetProcessImageFileNameW(procHandle
, path
, std::size(path
));
2214 return NS_ERROR_FAILURE
;
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
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);
2248 ScopedRtlShimWindow::~ScopedRtlShimWindow() {
2250 ::DestroyWindow(mWnd
);
2254 } // namespace widget
2255 } // namespace mozilla