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/Logging.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/ProfilerThreadSleep.h"
27 #include "mozilla/RefPtr.h"
28 #include "mozilla/SchedulerGroup.h"
29 #include "mozilla/WinHeaderOnlyUtils.h"
30 #include "mozilla/WindowsVersion.h"
31 #include "mozilla/Unused.h"
32 #include "nsIContentPolicy.h"
33 #include "WindowsUIUtils.h"
34 #include "nsContentUtils.h"
36 #include "mozilla/Logging.h"
39 #include "nsDirectoryServiceUtils.h"
40 #include "imgIContainer.h"
41 #include "imgITools.h"
42 #include "nsNetUtil.h"
43 #include "nsIOutputStream.h"
47 # include "nsIFaviconService.h"
49 #include "nsIDownloader.h"
50 #include "nsIChannel.h"
51 #include "nsIThread.h"
52 #include "MainThreadUtils.h"
53 #include "nsLookAndFeel.h"
54 #include "nsUnicharUtils.h"
55 #include "nsWindowsHelpers.h"
56 #include "WinContentSystemParameters.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
;
77 _msg, { #_msg, _msg } \
79 std::unordered_map
<UINT
, EventMsgInfo
> gAllEvents
= {
92 ENTRY(WM_GETTEXTLENGTH
),
95 ENTRY(WM_QUERYENDSESSION
),
99 ENTRY(WM_SYSCOLORCHANGE
),
100 ENTRY(WM_ENDSESSION
),
101 ENTRY(WM_SHOWWINDOW
),
102 ENTRY(WM_SETTINGCHANGE
),
103 ENTRY(WM_DEVMODECHANGE
),
104 ENTRY(WM_ACTIVATEAPP
),
105 ENTRY(WM_FONTCHANGE
),
106 ENTRY(WM_TIMECHANGE
),
107 ENTRY(WM_CANCELMODE
),
109 ENTRY(WM_MOUSEACTIVATE
),
110 ENTRY(WM_CHILDACTIVATE
),
112 ENTRY(WM_GETMINMAXINFO
),
114 ENTRY(WM_ICONERASEBKGND
),
115 ENTRY(WM_NEXTDLGCTL
),
116 ENTRY(WM_SPOOLERSTATUS
),
118 ENTRY(WM_MEASUREITEM
),
119 ENTRY(WM_DELETEITEM
),
120 ENTRY(WM_VKEYTOITEM
),
121 ENTRY(WM_CHARTOITEM
),
126 ENTRY(WM_QUERYDRAGICON
),
127 ENTRY(WM_COMPAREITEM
),
129 ENTRY(WM_COMPACTING
),
130 ENTRY(WM_COMMNOTIFY
),
131 ENTRY(WM_WINDOWPOSCHANGING
),
132 ENTRY(WM_WINDOWPOSCHANGED
),
135 ENTRY(WM_CANCELJOURNAL
),
137 ENTRY(WM_INPUTLANGCHANGEREQUEST
),
138 ENTRY(WM_INPUTLANGCHANGE
),
141 ENTRY(WM_USERCHANGED
),
142 ENTRY(WM_NOTIFYFORMAT
),
143 ENTRY(WM_CONTEXTMENU
),
144 ENTRY(WM_STYLECHANGING
),
145 ENTRY(WM_STYLECHANGED
),
146 ENTRY(WM_DISPLAYCHANGE
),
151 ENTRY(WM_NCCALCSIZE
),
154 ENTRY(WM_NCACTIVATE
),
155 ENTRY(WM_GETDLGCODE
),
157 ENTRY(WM_NCMOUSEMOVE
),
158 ENTRY(WM_NCLBUTTONDOWN
),
159 ENTRY(WM_NCLBUTTONUP
),
160 ENTRY(WM_NCLBUTTONDBLCLK
),
161 ENTRY(WM_NCRBUTTONDOWN
),
162 ENTRY(WM_NCRBUTTONUP
),
163 ENTRY(WM_NCRBUTTONDBLCLK
),
164 ENTRY(WM_NCMBUTTONDOWN
),
165 ENTRY(WM_NCMBUTTONUP
),
166 ENTRY(WM_NCMBUTTONDBLCLK
),
173 ENTRY(EM_LINESCROLL
),
174 ENTRY(EM_SCROLLCARET
),
177 ENTRY(EM_GETLINECOUNT
),
182 ENTRY(EM_LINELENGTH
),
183 ENTRY(EM_REPLACESEL
),
189 ENTRY(EM_LINEFROMCHAR
),
190 ENTRY(EM_SETTABSTOPS
),
191 ENTRY(EM_SETPASSWORDCHAR
),
192 ENTRY(EM_EMPTYUNDOBUFFER
),
193 ENTRY(EM_GETFIRSTVISIBLELINE
),
194 ENTRY(EM_SETREADONLY
),
195 ENTRY(EM_SETWORDBREAKPROC
),
196 ENTRY(EM_GETWORDBREAKPROC
),
197 ENTRY(EM_GETPASSWORDCHAR
),
198 ENTRY(EM_SETMARGINS
),
199 ENTRY(EM_GETMARGINS
),
200 ENTRY(EM_GETLIMITTEXT
),
201 ENTRY(EM_POSFROMCHAR
),
202 ENTRY(EM_CHARFROMPOS
),
203 ENTRY(EM_SETIMESTATUS
),
204 ENTRY(EM_GETIMESTATUS
),
208 ENTRY(SBM_SETRANGEREDRAW
),
210 ENTRY(SBM_ENABLE_ARROWS
),
211 ENTRY(SBM_SETSCROLLINFO
),
212 ENTRY(SBM_GETSCROLLINFO
),
217 ENTRY(WM_SYSKEYDOWN
),
220 ENTRY(WM_SYSDEADCHAR
),
222 ENTRY(WM_IME_STARTCOMPOSITION
),
223 ENTRY(WM_IME_ENDCOMPOSITION
),
224 ENTRY(WM_IME_COMPOSITION
),
225 ENTRY(WM_INITDIALOG
),
227 ENTRY(WM_SYSCOMMAND
),
232 ENTRY(WM_INITMENUPOPUP
),
233 ENTRY(WM_MENUSELECT
),
236 ENTRY(WM_MENURBUTTONUP
),
238 ENTRY(WM_MENUGETOBJECT
),
239 ENTRY(WM_UNINITMENUPOPUP
),
240 ENTRY(WM_MENUCOMMAND
),
241 ENTRY(WM_CHANGEUISTATE
),
242 ENTRY(WM_UPDATEUISTATE
),
243 ENTRY(WM_CTLCOLORMSGBOX
),
244 ENTRY(WM_CTLCOLOREDIT
),
245 ENTRY(WM_CTLCOLORLISTBOX
),
246 ENTRY(WM_CTLCOLORBTN
),
247 ENTRY(WM_CTLCOLORDLG
),
248 ENTRY(WM_CTLCOLORSCROLLBAR
),
249 ENTRY(WM_CTLCOLORSTATIC
),
250 ENTRY(CB_GETEDITSEL
),
252 ENTRY(CB_SETEDITSEL
),
254 ENTRY(CB_DELETESTRING
),
259 ENTRY(CB_GETLBTEXTLEN
),
260 ENTRY(CB_INSERTSTRING
),
261 ENTRY(CB_RESETCONTENT
),
262 ENTRY(CB_FINDSTRING
),
263 ENTRY(CB_SELECTSTRING
),
265 ENTRY(CB_SHOWDROPDOWN
),
266 ENTRY(CB_GETITEMDATA
),
267 ENTRY(CB_SETITEMDATA
),
268 ENTRY(CB_GETDROPPEDCONTROLRECT
),
269 ENTRY(CB_SETITEMHEIGHT
),
270 ENTRY(CB_GETITEMHEIGHT
),
271 ENTRY(CB_SETEXTENDEDUI
),
272 ENTRY(CB_GETEXTENDEDUI
),
273 ENTRY(CB_GETDROPPEDSTATE
),
274 ENTRY(CB_FINDSTRINGEXACT
),
277 ENTRY(CB_GETTOPINDEX
),
278 ENTRY(CB_SETTOPINDEX
),
279 ENTRY(CB_GETHORIZONTALEXTENT
),
280 ENTRY(CB_SETHORIZONTALEXTENT
),
281 ENTRY(CB_GETDROPPEDWIDTH
),
282 ENTRY(CB_SETDROPPEDWIDTH
),
283 ENTRY(CB_INITSTORAGE
),
286 ENTRY(LB_INSERTSTRING
),
287 ENTRY(LB_DELETESTRING
),
288 ENTRY(LB_SELITEMRANGEEX
),
289 ENTRY(LB_RESETCONTENT
),
295 ENTRY(LB_GETTEXTLEN
),
297 ENTRY(LB_SELECTSTRING
),
299 ENTRY(LB_GETTOPINDEX
),
300 ENTRY(LB_FINDSTRING
),
301 ENTRY(LB_GETSELCOUNT
),
302 ENTRY(LB_GETSELITEMS
),
303 ENTRY(LB_SETTABSTOPS
),
304 ENTRY(LB_GETHORIZONTALEXTENT
),
305 ENTRY(LB_SETHORIZONTALEXTENT
),
306 ENTRY(LB_SETCOLUMNWIDTH
),
308 ENTRY(LB_SETTOPINDEX
),
309 ENTRY(LB_GETITEMRECT
),
310 ENTRY(LB_GETITEMDATA
),
311 ENTRY(LB_SETITEMDATA
),
312 ENTRY(LB_SELITEMRANGE
),
313 ENTRY(LB_SETANCHORINDEX
),
314 ENTRY(LB_GETANCHORINDEX
),
315 ENTRY(LB_SETCARETINDEX
),
316 ENTRY(LB_GETCARETINDEX
),
317 ENTRY(LB_SETITEMHEIGHT
),
318 ENTRY(LB_GETITEMHEIGHT
),
319 ENTRY(LB_FINDSTRINGEXACT
),
323 ENTRY(LB_INITSTORAGE
),
324 ENTRY(LB_ITEMFROMPOINT
),
327 ENTRY(WM_LBUTTONDOWN
),
329 ENTRY(WM_LBUTTONDBLCLK
),
330 ENTRY(WM_RBUTTONDOWN
),
332 ENTRY(WM_RBUTTONDBLCLK
),
333 ENTRY(WM_MBUTTONDOWN
),
335 ENTRY(WM_MBUTTONDBLCLK
),
336 ENTRY(WM_MOUSEWHEEL
),
337 ENTRY(WM_MOUSEHWHEEL
),
338 ENTRY(WM_PARENTNOTIFY
),
339 ENTRY(WM_ENTERMENULOOP
),
340 ENTRY(WM_EXITMENULOOP
),
343 ENTRY(WM_CAPTURECHANGED
),
345 ENTRY(WM_POWERBROADCAST
),
346 ENTRY(WM_DEVICECHANGE
),
348 ENTRY(WM_MDIDESTROY
),
349 ENTRY(WM_MDIACTIVATE
),
350 ENTRY(WM_MDIRESTORE
),
352 ENTRY(WM_MDIMAXIMIZE
),
354 ENTRY(WM_MDICASCADE
),
355 ENTRY(WM_MDIICONARRANGE
),
356 ENTRY(WM_MDIGETACTIVE
),
357 ENTRY(WM_MDISETMENU
),
358 ENTRY(WM_ENTERSIZEMOVE
),
359 ENTRY(WM_EXITSIZEMOVE
),
361 ENTRY(WM_MDIREFRESHMENU
),
362 ENTRY(WM_IME_SETCONTEXT
),
363 ENTRY(WM_IME_NOTIFY
),
364 ENTRY(WM_IME_CONTROL
),
365 ENTRY(WM_IME_COMPOSITIONFULL
),
366 ENTRY(WM_IME_SELECT
),
368 ENTRY(WM_IME_REQUEST
),
369 ENTRY(WM_IME_KEYDOWN
),
371 ENTRY(WM_NCMOUSEHOVER
),
372 ENTRY(WM_MOUSEHOVER
),
373 ENTRY(WM_MOUSELEAVE
),
379 ENTRY(WM_RENDERFORMAT
),
380 ENTRY(WM_RENDERALLFORMATS
),
381 ENTRY(WM_DESTROYCLIPBOARD
),
382 ENTRY(WM_DRAWCLIPBOARD
),
383 ENTRY(WM_PAINTCLIPBOARD
),
384 ENTRY(WM_VSCROLLCLIPBOARD
),
385 ENTRY(WM_SIZECLIPBOARD
),
386 ENTRY(WM_ASKCBFORMATNAME
),
387 ENTRY(WM_CHANGECBCHAIN
),
388 ENTRY(WM_HSCROLLCLIPBOARD
),
389 ENTRY(WM_QUERYNEWPALETTE
),
390 ENTRY(WM_PALETTEISCHANGING
),
391 ENTRY(WM_PALETTECHANGED
),
394 ENTRY(WM_PRINTCLIENT
),
395 ENTRY(WM_THEMECHANGED
),
396 ENTRY(WM_HANDHELDFIRST
),
397 ENTRY(WM_HANDHELDLAST
),
400 ENTRY(WM_PENWINFIRST
),
401 ENTRY(WM_PENWINLAST
),
403 ENTRY(WM_DWMCOMPOSITIONCHANGED
),
404 ENTRY(WM_DWMNCRENDERINGCHANGED
),
405 ENTRY(WM_DWMCOLORIZATIONCOLORCHANGED
),
406 ENTRY(WM_DWMWINDOWMAXIMIZEDCHANGE
),
407 ENTRY(WM_DWMSENDICONICTHUMBNAIL
),
408 ENTRY(WM_DWMSENDICONICLIVEPREVIEWBITMAP
),
409 ENTRY(WM_TABLET_QUERYSYSTEMGESTURESTATUS
),
411 ENTRY(WM_GESTURENOTIFY
),
412 ENTRY(WM_GETTITLEBARINFOEX
),
413 {0x0, {nullptr, 0x0}}};
417 NS_IMPL_ISUPPORTS(myDownloadObserver
, nsIDownloadObserver
)
418 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady
, nsIFaviconDataCallback
)
420 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon
, nsIRunnable
)
421 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk
, nsIRunnable
)
423 const char FaviconHelper::kJumpListCacheDir
[] = "jumpListCache";
424 const char FaviconHelper::kShortcutCacheDir
[] = "shortcutCache";
426 // Prefix for path used by NT calls.
427 const wchar_t kNTPrefix
[] = L
"\\??\\";
428 const size_t kNTPrefixLen
= ArrayLength(kNTPrefix
) - 1;
430 struct CoTaskMemFreePolicy
{
431 void operator()(void* aPtr
) { ::CoTaskMemFree(aPtr
); }
434 SetThreadDpiAwarenessContextProc
WinUtils::sSetThreadDpiAwarenessContext
= NULL
;
435 EnableNonClientDpiScalingProc
WinUtils::sEnableNonClientDpiScaling
= NULL
;
436 GetSystemMetricsForDpiProc
WinUtils::sGetSystemMetricsForDpi
= NULL
;
437 bool WinUtils::sHasPackageIdentity
= false;
439 using GetDpiForWindowProc
= UINT(WINAPI
*)(HWND
);
440 static GetDpiForWindowProc sGetDpiForWindow
= NULL
;
443 void WinUtils::Initialize() {
444 // Dpi-Awareness is not supported with Win32k Lockdown enabled, so we don't
445 // initialize DPI-related members and assert later that nothing accidently
446 // uses these static members
447 if (IsWin10OrLater() && !IsWin32kLockedDown()) {
448 HMODULE user32Dll
= ::GetModuleHandleW(L
"user32");
450 auto getThreadDpiAwarenessContext
=
451 (decltype(GetThreadDpiAwarenessContext
)*)::GetProcAddress(
452 user32Dll
, "GetThreadDpiAwarenessContext");
453 auto areDpiAwarenessContextsEqual
=
454 (decltype(AreDpiAwarenessContextsEqual
)*)::GetProcAddress(
455 user32Dll
, "AreDpiAwarenessContextsEqual");
456 if (getThreadDpiAwarenessContext
&& areDpiAwarenessContextsEqual
&&
457 areDpiAwarenessContextsEqual(
458 getThreadDpiAwarenessContext(),
459 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
)) {
460 // Only per-monitor v1 requires these workarounds.
461 sEnableNonClientDpiScaling
=
462 (EnableNonClientDpiScalingProc
)::GetProcAddress(
463 user32Dll
, "EnableNonClientDpiScaling");
464 sSetThreadDpiAwarenessContext
=
465 (SetThreadDpiAwarenessContextProc
)::GetProcAddress(
466 user32Dll
, "SetThreadDpiAwarenessContext");
469 sGetSystemMetricsForDpi
= (GetSystemMetricsForDpiProc
)::GetProcAddress(
470 user32Dll
, "GetSystemMetricsForDpi");
472 (GetDpiForWindowProc
)::GetProcAddress(user32Dll
, "GetDpiForWindow");
476 if (IsWin8OrLater()) {
477 sHasPackageIdentity
= mozilla::HasPackageIdentity();
482 LRESULT WINAPI
WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd
, UINT msg
,
485 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
487 // NOTE: this function was copied out into the body of the pre-XUL skeleton
488 // UI window proc (PreXULSkeletonUI.cpp). If this function changes at any
489 // point, we should probably factor this out and use it from both locations.
490 if (msg
== WM_NCCREATE
&& sEnableNonClientDpiScaling
) {
491 sEnableNonClientDpiScaling(hWnd
);
493 return ::DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
497 void WinUtils::LogW(const wchar_t* fmt
, ...) {
498 va_list args
= nullptr;
499 if (!lstrlenW(fmt
)) {
503 int buflen
= _vscwprintf(fmt
, args
);
504 wchar_t* buffer
= new wchar_t[buflen
+ 1];
509 vswprintf(buffer
, buflen
, fmt
, args
);
512 // MSVC, including remote debug sessions
513 OutputDebugStringW(buffer
);
514 OutputDebugStringW(L
"\n");
517 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, nullptr, 0, nullptr, nullptr);
519 char* utf8
= new char[len
];
520 if (WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, utf8
, len
, nullptr,
523 printf("%s\n", utf8
);
524 NS_ASSERTION(gWindowsLog
,
525 "Called WinUtils Log() but Widget "
526 "log module doesn't exist!");
527 MOZ_LOG(gWindowsLog
, LogLevel::Error
, (utf8
));
535 void WinUtils::Log(const char* fmt
, ...) {
536 va_list args
= nullptr;
541 int buflen
= _vscprintf(fmt
, args
);
542 char* buffer
= new char[buflen
+ 1];
547 vsprintf(buffer
, fmt
, args
);
550 // MSVC, including remote debug sessions
551 OutputDebugStringA(buffer
);
552 OutputDebugStringW(L
"\n");
555 printf("%s\n", buffer
);
557 NS_ASSERTION(gWindowsLog
,
558 "Called WinUtils Log() but Widget "
559 "log module doesn't exist!");
560 MOZ_LOG(gWindowsLog
, LogLevel::Error
, (buffer
));
565 float WinUtils::SystemDPI() {
566 if (XRE_IsContentProcess()) {
567 return WinContentSystemParameters::GetSingleton()->SystemDPI();
569 // The result of GetDeviceCaps won't change dynamically, as it predates
570 // per-monitor DPI and support for on-the-fly resolution changes.
571 // Therefore, we only need to look it up once.
572 static float dpi
= 0;
574 HDC screenDC
= GetDC(nullptr);
575 dpi
= GetDeviceCaps(screenDC
, LOGPIXELSY
);
576 ReleaseDC(nullptr, screenDC
);
579 // Bug 1012487 - dpi can be 0 when the Screen DC is used off the
580 // main thread on windows. For now just assume a 100% DPI for this
583 return dpi
> 0 ? dpi
: 96;
587 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
591 MDT_EFFECTIVE_DPI
= 0,
594 MDT_DEFAULT
= MDT_EFFECTIVE_DPI
598 PROCESS_DPI_UNAWARE
= 0,
599 PROCESS_SYSTEM_DPI_AWARE
= 1,
600 PROCESS_PER_MONITOR_DPI_AWARE
= 2
601 } PROCESS_DPI_AWARENESS
;
604 typedef HRESULT(WINAPI
* GETDPIFORMONITORPROC
)(HMONITOR
, MONITOR_DPI_TYPE
, UINT
*,
607 typedef HRESULT(WINAPI
* GETPROCESSDPIAWARENESSPROC
)(HANDLE
,
608 PROCESS_DPI_AWARENESS
*);
610 GETDPIFORMONITORPROC sGetDpiForMonitor
;
611 GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness
;
613 static bool SlowIsPerMonitorDPIAware() {
614 // Intentionally leak the handle.
615 HMODULE shcore
= LoadLibraryEx(L
"shcore", NULL
, LOAD_LIBRARY_SEARCH_SYSTEM32
);
618 (GETDPIFORMONITORPROC
)GetProcAddress(shcore
, "GetDpiForMonitor");
619 sGetProcessDpiAwareness
= (GETPROCESSDPIAWARENESSPROC
)GetProcAddress(
620 shcore
, "GetProcessDpiAwareness");
622 PROCESS_DPI_AWARENESS dpiAwareness
;
623 return sGetDpiForMonitor
&& sGetProcessDpiAwareness
&&
625 sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness
)) &&
626 dpiAwareness
== PROCESS_PER_MONITOR_DPI_AWARE
;
630 bool WinUtils::IsPerMonitorDPIAware() {
631 if (XRE_IsContentProcess()) {
632 return WinContentSystemParameters::GetSingleton()->IsPerMonitorDPIAware();
634 static bool perMonitorDPIAware
= SlowIsPerMonitorDPIAware();
635 return perMonitorDPIAware
;
639 float WinUtils::MonitorDPI(HMONITOR aMonitor
) {
640 if (IsPerMonitorDPIAware()) {
641 UINT dpiX
, dpiY
= 96;
642 sGetDpiForMonitor(aMonitor
? aMonitor
: GetPrimaryMonitor(),
643 MDT_EFFECTIVE_DPI
, &dpiX
, &dpiY
);
647 // We're not per-monitor aware, use system DPI instead.
652 double WinUtils::LogToPhysFactor(HMONITOR aMonitor
) {
653 return MonitorDPI(aMonitor
) / 96.0;
657 int32_t WinUtils::LogToPhys(HMONITOR aMonitor
, double aValue
) {
658 return int32_t(NS_round(aValue
* LogToPhysFactor(aMonitor
)));
662 double WinUtils::LogToPhysFactor(HWND aWnd
) {
663 // if there's an ancestor window, we want to share its DPI setting
664 HWND ancestor
= ::GetAncestor(aWnd
, GA_ROOTOWNER
);
666 // The GetDpiForWindow api is not available everywhere where we run as
667 // per-monitor, but if it is available rely on it to tell us the scale
668 // factor of the window. See bug 1722085.
669 if (sGetDpiForWindow
) {
670 UINT dpi
= sGetDpiForWindow(ancestor
? ancestor
: aWnd
);
672 return static_cast<double>(dpi
) / 96.0;
675 return LogToPhysFactor(::MonitorFromWindow(ancestor
? ancestor
: aWnd
,
676 MONITOR_DEFAULTTOPRIMARY
));
681 WinUtils::GetPrimaryMonitor() {
682 const POINT pt
= {0, 0};
683 return ::MonitorFromPoint(pt
, MONITOR_DEFAULTTOPRIMARY
);
688 WinUtils::MonitorFromRect(const gfx::Rect
& rect
) {
689 // convert coordinates from desktop to device pixels for MonitorFromRect
691 IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor());
693 RECT globalWindowBounds
= {NSToIntRound(dpiScale
* rect
.X()),
694 NSToIntRound(dpiScale
* rect
.Y()),
695 NSToIntRound(dpiScale
* (rect
.XMost())),
696 NSToIntRound(dpiScale
* (rect
.YMost()))};
698 return ::MonitorFromRect(&globalWindowBounds
, MONITOR_DEFAULTTONEAREST
);
702 bool WinUtils::HasSystemMetricsForDpi() {
703 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
704 return (sGetSystemMetricsForDpi
!= NULL
);
708 int WinUtils::GetSystemMetricsForDpi(int nIndex
, UINT dpi
) {
709 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
710 if (HasSystemMetricsForDpi()) {
711 return sGetSystemMetricsForDpi(nIndex
, dpi
);
713 double scale
= IsPerMonitorDPIAware() ? dpi
/ SystemDPI() : 1.0;
714 return NSToIntRound(::GetSystemMetrics(nIndex
) * scale
);
719 gfx::MarginDouble
WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC aHdc
) {
721 return gfx::MarginDouble();
724 int pixelsPerInchY
= ::GetDeviceCaps(aHdc
, LOGPIXELSY
);
725 int marginTop
= ::GetDeviceCaps(aHdc
, PHYSICALOFFSETY
);
726 int printableAreaHeight
= ::GetDeviceCaps(aHdc
, VERTRES
);
727 int physicalHeight
= ::GetDeviceCaps(aHdc
, PHYSICALHEIGHT
);
729 double marginTopInch
= double(marginTop
) / pixelsPerInchY
;
731 double printableAreaHeightInch
= double(printableAreaHeight
) / pixelsPerInchY
;
732 double physicalHeightInch
= double(physicalHeight
) / pixelsPerInchY
;
733 double marginBottomInch
=
734 physicalHeightInch
- printableAreaHeightInch
- marginTopInch
;
736 int pixelsPerInchX
= ::GetDeviceCaps(aHdc
, LOGPIXELSX
);
737 int marginLeft
= ::GetDeviceCaps(aHdc
, PHYSICALOFFSETX
);
738 int printableAreaWidth
= ::GetDeviceCaps(aHdc
, HORZRES
);
739 int physicalWidth
= ::GetDeviceCaps(aHdc
, PHYSICALWIDTH
);
741 double marginLeftInch
= double(marginLeft
) / pixelsPerInchX
;
743 double printableAreaWidthInch
= double(printableAreaWidth
) / pixelsPerInchX
;
744 double physicalWidthInch
= double(physicalWidth
) / pixelsPerInchX
;
745 double marginRightInch
=
746 physicalWidthInch
- printableAreaWidthInch
- marginLeftInch
;
748 return gfx::MarginDouble(marginTopInch
, marginRightInch
, marginBottomInch
,
754 a11y::LocalAccessible
* WinUtils::GetRootAccessibleForHWND(HWND aHwnd
) {
755 nsWindow
* window
= GetNSWindowPtr(aHwnd
);
760 return window
->GetAccessible();
762 #endif // ACCESSIBILITY
765 bool WinUtils::PeekMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
766 UINT aLastMessage
, UINT aOption
) {
767 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
770 HRESULT hr
= msgPump
->PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
,
772 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
775 return ::PeekMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, aOption
);
779 bool WinUtils::GetMessage(LPMSG aMsg
, HWND aWnd
, UINT aFirstMessage
,
781 RefPtr
<ITfMessagePump
> msgPump
= TSFTextStore::GetMessagePump();
785 msgPump
->GetMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
, &ret
);
786 NS_ENSURE_TRUE(SUCCEEDED(hr
), false);
789 return ::GetMessageW(aMsg
, aWnd
, aFirstMessage
, aLastMessage
);
792 #if defined(ACCESSIBILITY)
793 static DWORD
GetWaitFlags() {
794 DWORD result
= MWMO_INPUTAVAILABLE
;
795 if (XRE_IsContentProcess()) {
796 result
|= MWMO_ALERTABLE
;
803 void WinUtils::WaitForMessage(DWORD aTimeoutMs
) {
804 #if defined(ACCESSIBILITY)
805 static const DWORD waitFlags
= GetWaitFlags();
807 const DWORD waitFlags
= MWMO_INPUTAVAILABLE
;
810 const DWORD waitStart
= ::GetTickCount();
813 if (aTimeoutMs
!= INFINITE
) {
814 elapsed
= ::GetTickCount() - waitStart
;
816 if (elapsed
>= aTimeoutMs
) {
821 AUTO_PROFILER_THREAD_SLEEP
;
822 result
= ::MsgWaitForMultipleObjectsEx(0, NULL
, aTimeoutMs
- elapsed
,
823 MOZ_QS_ALLEVENT
, waitFlags
);
825 NS_WARNING_ASSERTION(result
!= WAIT_FAILED
, "Wait failed");
826 if (result
== WAIT_TIMEOUT
) {
829 #if defined(ACCESSIBILITY)
830 if (result
== WAIT_IO_COMPLETION
) {
831 if (NS_IsMainThread()) {
832 // We executed an APC that would have woken up the hang monitor. Since
833 // there are no more APCs pending and we are now going to sleep again,
834 // we should notify the hang monitor.
835 mozilla::BackgroundHangMonitor().NotifyWait();
839 #endif // defined(ACCESSIBILITY)
841 // Sent messages (via SendMessage and friends) are processed differently
842 // than queued messages (via PostMessage); the destination window procedure
843 // of the sent message is called during (Get|Peek)Message. Since PeekMessage
844 // does not tell us whether it processed any sent messages, we need to query
845 // this ahead of time.
846 bool haveSentMessagesPending
=
847 (HIWORD(::GetQueueStatus(QS_SENDMESSAGE
)) & QS_SENDMESSAGE
) != 0;
850 if (haveSentMessagesPending
||
851 ::PeekMessageW(&msg
, nullptr, 0, 0, PM_NOREMOVE
)) {
854 // The message is intended for another thread that has been synchronized
855 // with our input queue; yield to give other threads an opportunity to
856 // process the message. This should prevent busy waiting if resumed due
857 // to another thread's message.
863 bool WinUtils::GetRegistryKey(HKEY aRoot
, char16ptr_t aKeyName
,
864 char16ptr_t aValueName
, wchar_t* aBuffer
,
865 DWORD aBufferLength
) {
866 MOZ_ASSERT(aKeyName
, "The key name is NULL");
870 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_32KEY
, &key
);
871 if (result
!= ERROR_SUCCESS
) {
873 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
);
874 if (result
!= ERROR_SUCCESS
) {
880 result
= ::RegQueryValueExW(key
, aValueName
, nullptr, &type
, (BYTE
*)aBuffer
,
883 if (result
!= ERROR_SUCCESS
|| (type
!= REG_SZ
&& type
!= REG_EXPAND_SZ
)) {
887 aBuffer
[aBufferLength
/ sizeof(*aBuffer
) - 1] = 0;
893 bool WinUtils::HasRegistryKey(HKEY aRoot
, char16ptr_t aKeyName
) {
894 MOZ_ASSERT(aRoot
, "aRoot must not be NULL");
895 MOZ_ASSERT(aKeyName
, "aKeyName must not be NULL");
898 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_32KEY
, &key
);
899 if (result
!= ERROR_SUCCESS
) {
901 ::RegOpenKeyExW(aRoot
, aKeyName
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
);
902 if (result
!= ERROR_SUCCESS
) {
911 HWND
WinUtils::GetTopLevelHWND(HWND aWnd
, bool aStopIfNotChild
,
912 bool aStopIfNotPopup
) {
914 HWND topWnd
= nullptr;
919 if (aStopIfNotChild
) {
920 DWORD_PTR style
= ::GetWindowLongPtrW(curWnd
, GWL_STYLE
);
922 VERIFY_WINDOW_STYLE(style
);
924 if (!(style
& WS_CHILD
)) // first top-level window
928 HWND upWnd
= ::GetParent(curWnd
); // Parent or owner (if has no parent)
930 // GetParent will only return the owner if the passed in window
931 // has the WS_POPUP style.
932 if (!upWnd
&& !aStopIfNotPopup
) {
933 upWnd
= ::GetWindow(curWnd
, GW_OWNER
);
941 static const wchar_t* GetNSWindowPropName() {
942 static wchar_t sPropName
[40] = L
"";
944 _snwprintf(sPropName
, 39, L
"MozillansIWidgetPtr%u",
945 ::GetCurrentProcessId());
946 sPropName
[39] = '\0';
952 bool WinUtils::SetNSWindowBasePtr(HWND aWnd
, nsWindow
* aWidget
) {
954 ::RemovePropW(aWnd
, GetNSWindowPropName());
957 return ::SetPropW(aWnd
, GetNSWindowPropName(), (HANDLE
)aWidget
);
961 nsWindow
* WinUtils::GetNSWindowBasePtr(HWND aWnd
) {
962 return static_cast<nsWindow
*>(::GetPropW(aWnd
, GetNSWindowPropName()));
966 nsWindow
* WinUtils::GetNSWindowPtr(HWND aWnd
) {
967 return static_cast<nsWindow
*>(::GetPropW(aWnd
, GetNSWindowPropName()));
970 static BOOL CALLBACK
AddMonitor(HMONITOR
, HDC
, LPRECT
, LPARAM aParam
) {
971 (*(int32_t*)aParam
)++;
976 int32_t WinUtils::GetMonitorCount() {
977 int32_t monitorCount
= 0;
978 EnumDisplayMonitors(nullptr, nullptr, AddMonitor
, (LPARAM
)&monitorCount
);
983 bool WinUtils::IsOurProcessWindow(HWND aWnd
) {
988 ::GetWindowThreadProcessId(aWnd
, &processId
);
989 return (processId
== ::GetCurrentProcessId());
993 HWND
WinUtils::FindOurProcessWindow(HWND aWnd
) {
994 for (HWND wnd
= ::GetParent(aWnd
); wnd
; wnd
= ::GetParent(wnd
)) {
995 if (IsOurProcessWindow(wnd
)) {
1002 static bool IsPointInWindow(HWND aWnd
, const POINT
& aPointInScreen
) {
1004 if (!::GetWindowRect(aWnd
, &bounds
)) {
1008 return (aPointInScreen
.x
>= bounds
.left
&& aPointInScreen
.x
< bounds
.right
&&
1009 aPointInScreen
.y
>= bounds
.top
&& aPointInScreen
.y
< bounds
.bottom
);
1013 * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
1014 * forground in this context) of aWnd.
1017 static HWND
FindTopmostWindowAtPoint(HWND aWnd
, const POINT
& aPointInScreen
) {
1018 if (!::IsWindowVisible(aWnd
) || !IsPointInWindow(aWnd
, aPointInScreen
)) {
1022 HWND childWnd
= ::GetTopWindow(aWnd
);
1024 HWND topmostWnd
= FindTopmostWindowAtPoint(childWnd
, aPointInScreen
);
1028 childWnd
= ::GetNextWindow(childWnd
, GW_HWNDNEXT
);
1034 struct FindOurWindowAtPointInfo
{
1035 POINT mInPointInScreen
;
1039 static BOOL CALLBACK
FindOurWindowAtPointCallback(HWND aWnd
, LPARAM aLPARAM
) {
1040 if (!WinUtils::IsOurProcessWindow(aWnd
)) {
1041 // This isn't one of our top-level windows; continue enumerating.
1045 // Get the top-most child window under the point. If there's no child
1046 // window, and the point is within the top-level window, then the top-level
1047 // window will be returned. (This is the usual case. A child window
1048 // would be returned for plugins.)
1049 FindOurWindowAtPointInfo
* info
=
1050 reinterpret_cast<FindOurWindowAtPointInfo
*>(aLPARAM
);
1051 HWND childWnd
= FindTopmostWindowAtPoint(aWnd
, info
->mInPointInScreen
);
1053 // This window doesn't contain the point; continue enumerating.
1057 // Return the HWND and stop enumerating.
1058 info
->mOutWnd
= childWnd
;
1063 HWND
WinUtils::FindOurWindowAtPoint(const POINT
& aPointInScreen
) {
1064 FindOurWindowAtPointInfo info
;
1065 info
.mInPointInScreen
= aPointInScreen
;
1066 info
.mOutWnd
= nullptr;
1068 // This will enumerate all top-level windows in order from top to bottom.
1069 EnumWindows(FindOurWindowAtPointCallback
, reinterpret_cast<LPARAM
>(&info
));
1070 return info
.mOutWnd
;
1074 UINT
WinUtils::GetInternalMessage(UINT aNativeMessage
) {
1075 switch (aNativeMessage
) {
1077 return MOZ_WM_MOUSEVWHEEL
;
1078 case WM_MOUSEHWHEEL
:
1079 return MOZ_WM_MOUSEHWHEEL
;
1081 return MOZ_WM_VSCROLL
;
1083 return MOZ_WM_HSCROLL
;
1085 return aNativeMessage
;
1090 UINT
WinUtils::GetNativeMessage(UINT aInternalMessage
) {
1091 switch (aInternalMessage
) {
1092 case MOZ_WM_MOUSEVWHEEL
:
1093 return WM_MOUSEWHEEL
;
1094 case MOZ_WM_MOUSEHWHEEL
:
1095 return WM_MOUSEHWHEEL
;
1096 case MOZ_WM_VSCROLL
:
1098 case MOZ_WM_HSCROLL
:
1101 return aInternalMessage
;
1106 uint16_t WinUtils::GetMouseInputSource() {
1107 int32_t inputSource
= dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE
;
1108 LPARAM lParamExtraInfo
= ::GetMessageExtraInfo();
1109 if ((lParamExtraInfo
& TABLET_INK_SIGNATURE
) == TABLET_INK_CHECK
) {
1110 inputSource
= (lParamExtraInfo
& TABLET_INK_TOUCH
)
1111 ? dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
1112 : dom::MouseEvent_Binding::MOZ_SOURCE_PEN
;
1114 return static_cast<uint16_t>(inputSource
);
1118 uint16_t WinUtils::GetMousePointerID() {
1119 LPARAM lParamExtraInfo
= ::GetMessageExtraInfo();
1120 return lParamExtraInfo
& TABLET_INK_ID_MASK
;
1124 bool WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage
) {
1125 const uint32_t MOZ_T_I_SIGNATURE
= TABLET_INK_TOUCH
| TABLET_INK_SIGNATURE
;
1126 const uint32_t MOZ_T_I_CHECK_TCH
= TABLET_INK_TOUCH
| TABLET_INK_CHECK
;
1127 return ((aEventMessage
== eMouseMove
|| aEventMessage
== eMouseDown
||
1128 aEventMessage
== eMouseUp
|| aEventMessage
== eMouseAuxClick
||
1129 aEventMessage
== eMouseDoubleClick
) &&
1130 (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE
) == MOZ_T_I_CHECK_TCH
);
1134 MSG
WinUtils::InitMSG(UINT aMessage
, WPARAM wParam
, LPARAM lParam
, HWND aWnd
) {
1136 msg
.message
= aMessage
;
1137 msg
.wParam
= wParam
;
1138 msg
.lParam
= lParam
;
1143 static BOOL WINAPI
EnumFirstChild(HWND hwnd
, LPARAM lParam
) {
1144 *((HWND
*)lParam
) = hwnd
;
1149 void WinUtils::InvalidatePluginAsWorkaround(nsIWidget
* aWidget
,
1150 const LayoutDeviceIntRect
& aRect
) {
1151 aWidget
->Invalidate(aRect
);
1153 // XXX - Even more evil workaround!! See bug 762948, flash's bottom
1154 // level sandboxed window doesn't seem to get our invalidate. We send
1155 // an invalidate to it manually. This is totally specialized for this
1156 // bug, for other child window structures this will just be a more or
1157 // less bogus invalidate but since that should not have any bad
1158 // side-effects this will have to do for now.
1159 HWND current
= (HWND
)aWidget
->GetNativeData(NS_NATIVE_WINDOW
);
1164 ::GetWindowRect(current
, &parentRect
);
1166 HWND next
= current
;
1169 ::EnumChildWindows(current
, &EnumFirstChild
, (LPARAM
)&next
);
1170 ::GetWindowRect(next
, &windowRect
);
1171 // This is relative to the screen, adjust it to be relative to the
1172 // window we're reconfiguring.
1173 windowRect
.left
-= parentRect
.left
;
1174 windowRect
.top
-= parentRect
.top
;
1175 } while (next
!= current
&& windowRect
.top
== 0 && windowRect
.left
== 0);
1177 if (windowRect
.top
== 0 && windowRect
.left
== 0) {
1179 rect
.left
= aRect
.X();
1180 rect
.top
= aRect
.Y();
1181 rect
.right
= aRect
.XMost();
1182 rect
.bottom
= aRect
.YMost();
1184 ::InvalidateRect(next
, &rect
, FALSE
);
1189 /************************************************************************
1190 * Constructs as AsyncFaviconDataReady Object
1191 * @param aIOThread : the thread which performs the action
1192 * @param aURLShortcut : Differentiates between (false)Jumplistcache and
1193 * (true)Shortcutcache
1194 * @param aRunnable : Executed in the aIOThread when the favicon cache is
1196 ************************************************************************/
1198 AsyncFaviconDataReady::AsyncFaviconDataReady(
1199 nsIURI
* aNewURI
, nsCOMPtr
<nsIThread
>& aIOThread
, const bool aURLShortcut
,
1200 already_AddRefed
<nsIRunnable
> aRunnable
)
1202 mIOThread(aIOThread
),
1203 mRunnable(aRunnable
),
1204 mURLShortcut(aURLShortcut
) {}
1207 myDownloadObserver::OnDownloadComplete(nsIDownloader
* downloader
,
1208 nsIRequest
* request
, nsresult status
,
1213 nsresult
AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
1214 if (!mURLShortcut
) {
1218 nsCOMPtr
<nsIFile
> icoFile
;
1220 FaviconHelper::GetOutputIconPath(mNewURI
, icoFile
, mURLShortcut
);
1221 NS_ENSURE_SUCCESS(rv
, rv
);
1223 nsCOMPtr
<nsIURI
> mozIconURI
;
1224 rv
= NS_NewURI(getter_AddRefs(mozIconURI
), "moz-icon://.html?size=32");
1225 if (NS_FAILED(rv
)) {
1229 nsCOMPtr
<nsIChannel
> channel
;
1230 rv
= NS_NewChannel(getter_AddRefs(channel
), mozIconURI
,
1231 nsContentUtils::GetSystemPrincipal(),
1232 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
1233 nsIContentPolicy::TYPE_INTERNAL_IMAGE
);
1235 NS_ENSURE_SUCCESS(rv
, rv
);
1237 nsCOMPtr
<nsIDownloadObserver
> downloadObserver
= new myDownloadObserver
;
1238 nsCOMPtr
<nsIStreamListener
> listener
;
1239 rv
= NS_NewDownloader(getter_AddRefs(listener
), downloadObserver
, icoFile
);
1240 NS_ENSURE_SUCCESS(rv
, rv
);
1242 return channel
->AsyncOpen(listener
);
1246 AsyncFaviconDataReady::OnComplete(nsIURI
* aFaviconURI
, uint32_t aDataLen
,
1247 const uint8_t* aData
,
1248 const nsACString
& aMimeType
,
1250 if (!aDataLen
|| !aData
) {
1252 OnFaviconDataNotAvailable();
1258 nsCOMPtr
<nsIFile
> icoFile
;
1260 FaviconHelper::GetOutputIconPath(mNewURI
, icoFile
, mURLShortcut
);
1261 NS_ENSURE_SUCCESS(rv
, rv
);
1264 rv
= icoFile
->GetPath(path
);
1265 NS_ENSURE_SUCCESS(rv
, rv
);
1267 // Decode the image from the format it was returned to us in (probably PNG)
1268 nsCOMPtr
<imgIContainer
> container
;
1269 nsCOMPtr
<imgITools
> imgtool
= do_CreateInstance("@mozilla.org/image/tools;1");
1270 rv
= imgtool
->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData
),
1271 aDataLen
, aMimeType
,
1272 getter_AddRefs(container
));
1273 NS_ENSURE_SUCCESS(rv
, rv
);
1275 RefPtr
<SourceSurface
> surface
= container
->GetFrame(
1276 imgIContainer::FRAME_FIRST
,
1277 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
1278 NS_ENSURE_TRUE(surface
, NS_ERROR_FAILURE
);
1280 RefPtr
<DataSourceSurface
> dataSurface
;
1284 (surface
->GetSize().width
< 48 || surface
->GetSize().height
< 48)) {
1285 // Create a 48x48 surface and paint the icon into the central rect.
1286 size
.width
= std::max(surface
->GetSize().width
, 48);
1287 size
.height
= std::max(surface
->GetSize().height
, 48);
1289 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1290 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
1292 DataSourceSurface::MappedSurface map
;
1293 if (!dataSurface
->Map(DataSourceSurface::MapType::WRITE
, &map
)) {
1294 return NS_ERROR_FAILURE
;
1297 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1298 BackendType::CAIRO
, map
.mData
, dataSurface
->GetSize(), map
.mStride
,
1299 dataSurface
->GetFormat());
1301 gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
1302 "CreateDrawTargetForData";
1303 return NS_ERROR_OUT_OF_MEMORY
;
1305 dt
->FillRect(Rect(0, 0, size
.width
, size
.height
),
1306 ColorPattern(ToDeviceColor(sRGBColor::OpaqueWhite())));
1308 point
.x
= (size
.width
- surface
->GetSize().width
) / 2;
1309 point
.y
= (size
.height
- surface
->GetSize().height
) / 2;
1310 dt
->DrawSurface(surface
,
1311 Rect(point
.x
, point
.y
, surface
->GetSize().width
,
1312 surface
->GetSize().height
),
1313 Rect(Point(0, 0), Size(surface
->GetSize().width
,
1314 surface
->GetSize().height
)));
1316 dataSurface
->Unmap();
1318 // By using the input image surface's size, we may end up encoding
1319 // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
1320 // Windows will resize appropriately for us. If we want to encode ourselves
1321 // one day because we like our resizing better, we'd have to manually
1322 // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
1323 // SM_CYSMICON. We don't support resizing images asynchronously at the
1324 // moment anyway so getting the DPI aware icon size won't help.
1325 size
.width
= surface
->GetSize().width
;
1326 size
.height
= surface
->GetSize().height
;
1327 dataSurface
= surface
->GetDataSurface();
1328 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
1331 // Allocate a new buffer that we own and can use out of line in
1333 UniquePtr
<uint8_t[]> data
= SurfaceToPackedBGRA(dataSurface
);
1335 return NS_ERROR_OUT_OF_MEMORY
;
1337 int32_t stride
= 4 * size
.width
;
1339 // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
1340 nsCOMPtr
<nsIRunnable
> event
=
1341 new AsyncEncodeAndWriteIcon(path
, std::move(data
), stride
, size
.width
,
1342 size
.height
, mRunnable
.forget());
1343 mIOThread
->Dispatch(event
, NS_DISPATCH_NORMAL
);
1349 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
1351 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
1352 const nsAString
& aIconPath
, UniquePtr
<uint8_t[]> aBuffer
, uint32_t aStride
,
1353 uint32_t aWidth
, uint32_t aHeight
, already_AddRefed
<nsIRunnable
> aRunnable
)
1354 : mIconPath(aIconPath
),
1355 mBuffer(std::move(aBuffer
)),
1356 mRunnable(aRunnable
),
1361 NS_IMETHODIMP
AsyncEncodeAndWriteIcon::Run() {
1362 MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");
1364 // Note that since we're off the main thread we can't use
1365 // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
1366 RefPtr
<DataSourceSurface
> surface
= Factory::CreateWrappingDataSourceSurface(
1367 mBuffer
.get(), mStride
, IntSize(mWidth
, mHeight
),
1368 SurfaceFormat::B8G8R8A8
);
1370 FILE* file
= _wfopen(mIconPath
.get(), L
"wb");
1372 // Maybe the directory doesn't exist; try creating it, then fopen again.
1373 nsresult rv
= NS_ERROR_FAILURE
;
1374 nsCOMPtr
<nsIFile
> comFile
= do_CreateInstance("@mozilla.org/file/local;1");
1376 rv
= comFile
->InitWithPath(mIconPath
);
1377 if (NS_SUCCEEDED(rv
)) {
1378 nsCOMPtr
<nsIFile
> dirPath
;
1379 comFile
->GetParent(getter_AddRefs(dirPath
));
1381 rv
= dirPath
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
1382 if (NS_SUCCEEDED(rv
) || rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
1383 file
= _wfopen(mIconPath
.get(), L
"wb");
1385 rv
= NS_ERROR_FAILURE
;
1395 nsresult rv
= gfxUtils::EncodeSourceSurface(surface
, ImageType::ICO
, u
""_ns
,
1396 gfxUtils::eBinaryEncode
, file
);
1398 NS_ENSURE_SUCCESS(rv
, rv
);
1406 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {}
1408 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
1410 : mIgnoreRecent(aIgnoreRecent
) {
1411 // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
1412 // threads, as it reads a pref, so cache its value here.
1413 mIcoNoDeleteSeconds
= FaviconHelper::GetICOCacheSecondsTimeout() + 600;
1415 // Prepare the profile directory cache on the main thread, to ensure we wont
1416 // do this on non-main threads.
1417 Unused
<< NS_GetSpecialDirectory("ProfLDS",
1418 getter_AddRefs(mJumpListCacheDir
));
1421 NS_IMETHODIMP
AsyncDeleteAllFaviconsFromDisk::Run() {
1422 if (!mJumpListCacheDir
) {
1423 return NS_ERROR_FAILURE
;
1425 // Construct the path of our jump list cache
1426 nsresult rv
= mJumpListCacheDir
->AppendNative(
1427 nsDependentCString(FaviconHelper::kJumpListCacheDir
));
1428 NS_ENSURE_SUCCESS(rv
, rv
);
1430 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
1431 rv
= mJumpListCacheDir
->GetDirectoryEntries(getter_AddRefs(entries
));
1432 NS_ENSURE_SUCCESS(rv
, rv
);
1434 // Loop through each directory entry and remove all ICO files found
1436 nsCOMPtr
<nsIFile
> currFile
;
1437 if (NS_FAILED(entries
->GetNextFile(getter_AddRefs(currFile
))) || !currFile
)
1441 if (NS_FAILED(currFile
->GetPath(path
))) continue;
1443 if (StringTail(path
, 4).LowerCaseEqualsASCII(".ico")) {
1444 // Check if the cached ICO file exists
1446 if (NS_FAILED(currFile
->Exists(&exists
)) || !exists
) continue;
1448 if (mIgnoreRecent
) {
1449 // Check to make sure the icon wasn't just recently created.
1450 // If it was created recently, don't delete it yet.
1451 int64_t fileModTime
= 0;
1452 rv
= currFile
->GetLastModifiedTime(&fileModTime
);
1453 fileModTime
/= PR_MSEC_PER_SEC
;
1454 // If the icon is older than the regeneration time (+ 10 min to be
1455 // safe), then it's old and we can get rid of it.
1456 // This code is only hit directly after a regeneration.
1457 int64_t nowTime
= PR_Now() / int64_t(PR_USEC_PER_SEC
);
1458 if (NS_FAILED(rv
) || (nowTime
- fileModTime
) < mIcoNoDeleteSeconds
) {
1463 // We found an ICO file that exists, so we should remove it
1464 currFile
->Remove(false);
1471 AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() {}
1474 * (static) If the data is available, will return the path on disk where
1475 * the favicon for page aFaviconPageURI is stored. If the favicon does not
1476 * exist, or its cache is expired, this method will kick off an async request
1477 * for the icon so that next time the method is called it will be available.
1478 * @param aFaviconPageURI The URI of the page to obtain
1479 * @param aICOFilePath The path of the icon file
1480 * @param aIOThread The thread to perform the Fetch on
1481 * @param aURLShortcut to distinguish between jumplistcache(false) and
1482 * shortcutcache(true)
1483 * @param aRunnable Executed in the aIOThread when the favicon cache is
1486 nsresult
FaviconHelper::ObtainCachedIconFile(
1487 nsCOMPtr
<nsIURI
> aFaviconPageURI
, nsString
& aICOFilePath
,
1488 nsCOMPtr
<nsIThread
>& aIOThread
, bool aURLShortcut
,
1489 already_AddRefed
<nsIRunnable
> aRunnable
) {
1490 nsCOMPtr
<nsIRunnable
> runnable
= aRunnable
;
1491 // Obtain the ICO file path
1492 nsCOMPtr
<nsIFile
> icoFile
;
1493 nsresult rv
= GetOutputIconPath(aFaviconPageURI
, icoFile
, aURLShortcut
);
1494 NS_ENSURE_SUCCESS(rv
, rv
);
1496 // Check if the cached ICO file already exists
1498 rv
= icoFile
->Exists(&exists
);
1499 NS_ENSURE_SUCCESS(rv
, rv
);
1502 // Obtain the file's last modification date in seconds
1503 int64_t fileModTime
= 0;
1504 rv
= icoFile
->GetLastModifiedTime(&fileModTime
);
1505 fileModTime
/= PR_MSEC_PER_SEC
;
1506 int32_t icoReCacheSecondsTimeout
= GetICOCacheSecondsTimeout();
1507 int64_t nowTime
= PR_Now() / int64_t(PR_USEC_PER_SEC
);
1509 // If the last mod call failed or the icon is old then re-cache it
1510 // This check is in case the favicon of a page changes
1511 // the next time we try to build the jump list, the data will be available.
1512 if (NS_FAILED(rv
) || (nowTime
- fileModTime
) > icoReCacheSecondsTimeout
) {
1513 CacheIconFileFromFaviconURIAsync(aFaviconPageURI
, icoFile
, aIOThread
,
1514 aURLShortcut
, runnable
.forget());
1515 return NS_ERROR_NOT_AVAILABLE
;
1518 // The file does not exist yet, obtain it async from the favicon service so
1519 // that the next time we try to build the jump list it'll be available.
1520 CacheIconFileFromFaviconURIAsync(aFaviconPageURI
, icoFile
, aIOThread
,
1521 aURLShortcut
, runnable
.forget());
1522 return NS_ERROR_NOT_AVAILABLE
;
1525 // The icoFile is filled with a path that exists, get its path
1526 rv
= icoFile
->GetPath(aICOFilePath
);
1530 nsresult
FaviconHelper::HashURI(nsCOMPtr
<nsICryptoHash
>& aCryptoHash
,
1531 nsIURI
* aUri
, nsACString
& aUriHash
) {
1532 if (!aUri
) return NS_ERROR_INVALID_ARG
;
1535 nsresult rv
= aUri
->GetSpec(spec
);
1536 NS_ENSURE_SUCCESS(rv
, rv
);
1539 aCryptoHash
= do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID
, &rv
);
1540 NS_ENSURE_SUCCESS(rv
, rv
);
1543 rv
= aCryptoHash
->Init(nsICryptoHash::MD5
);
1544 NS_ENSURE_SUCCESS(rv
, rv
);
1545 rv
= aCryptoHash
->Update(
1546 reinterpret_cast<const uint8_t*>(spec
.BeginReading()), spec
.Length());
1547 NS_ENSURE_SUCCESS(rv
, rv
);
1548 rv
= aCryptoHash
->Finish(true, aUriHash
);
1549 NS_ENSURE_SUCCESS(rv
, rv
);
1554 // (static) Obtains the ICO file for the favicon at page aFaviconPageURI
1555 // If successful, the file path on disk is in the format:
1556 // <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
1557 nsresult
FaviconHelper::GetOutputIconPath(nsCOMPtr
<nsIURI
> aFaviconPageURI
,
1558 nsCOMPtr
<nsIFile
>& aICOFile
,
1559 bool aURLShortcut
) {
1560 // Hash the input URI and replace any / with _
1561 nsAutoCString inputURIHash
;
1562 nsCOMPtr
<nsICryptoHash
> cryptoHash
;
1563 nsresult rv
= HashURI(cryptoHash
, aFaviconPageURI
, inputURIHash
);
1564 NS_ENSURE_SUCCESS(rv
, rv
);
1565 char* cur
= inputURIHash
.BeginWriting();
1566 char* end
= inputURIHash
.EndWriting();
1567 for (; cur
< end
; ++cur
) {
1573 // Obtain the local profile directory and construct the output icon file path
1574 rv
= NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile
));
1575 NS_ENSURE_SUCCESS(rv
, rv
);
1577 rv
= aICOFile
->AppendNative(nsDependentCString(kJumpListCacheDir
));
1579 rv
= aICOFile
->AppendNative(nsDependentCString(kShortcutCacheDir
));
1580 NS_ENSURE_SUCCESS(rv
, rv
);
1582 // Append the icon extension
1583 inputURIHash
.AppendLiteral(".ico");
1584 rv
= aICOFile
->AppendNative(inputURIHash
);
1589 // (static) Asynchronously creates a cached ICO file on disk for the favicon of
1590 // page aFaviconPageURI and stores it to disk at the path of aICOFile.
1591 nsresult
FaviconHelper::CacheIconFileFromFaviconURIAsync(
1592 nsCOMPtr
<nsIURI
> aFaviconPageURI
, nsCOMPtr
<nsIFile
> aICOFile
,
1593 nsCOMPtr
<nsIThread
>& aIOThread
, bool aURLShortcut
,
1594 already_AddRefed
<nsIRunnable
> aRunnable
) {
1595 nsCOMPtr
<nsIRunnable
> runnable
= aRunnable
;
1597 // Obtain the favicon service and get the favicon for the specified page
1598 nsCOMPtr
<nsIFaviconService
> favIconSvc(
1599 do_GetService("@mozilla.org/browser/favicon-service;1"));
1600 NS_ENSURE_TRUE(favIconSvc
, NS_ERROR_FAILURE
);
1602 nsCOMPtr
<nsIFaviconDataCallback
> callback
=
1603 new mozilla::widget::AsyncFaviconDataReady(
1604 aFaviconPageURI
, aIOThread
, aURLShortcut
, runnable
.forget());
1606 favIconSvc
->GetFaviconDataForPage(aFaviconPageURI
, callback
, 0);
1611 // Obtains the jump list 'ICO cache timeout in seconds' pref
1612 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
1613 // Only obtain the setting at most once from the pref service.
1614 // In the rare case that 2 threads call this at the same
1615 // time it is no harm and we will simply obtain the pref twice.
1616 // None of the taskbar list prefs are currently updated via a
1617 // pref observer so I think this should suffice.
1618 const int32_t kSecondsPerDay
= 86400;
1619 static bool alreadyObtained
= false;
1620 static int32_t icoReCacheSecondsTimeout
= kSecondsPerDay
;
1621 if (alreadyObtained
) {
1622 return icoReCacheSecondsTimeout
;
1626 const char PREF_ICOTIMEOUT
[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1627 icoReCacheSecondsTimeout
=
1628 Preferences::GetInt(PREF_ICOTIMEOUT
, kSecondsPerDay
);
1629 alreadyObtained
= true;
1630 return icoReCacheSecondsTimeout
;
1634 bool WinUtils::GetShellItemPath(IShellItem
* aItem
, nsString
& aResultString
) {
1635 NS_ENSURE_TRUE(aItem
, false);
1636 LPWSTR str
= nullptr;
1637 if (FAILED(aItem
->GetDisplayName(SIGDN_FILESYSPATH
, &str
))) return false;
1638 aResultString
.Assign(str
);
1640 return !aResultString
.IsEmpty();
1644 LayoutDeviceIntRegion
WinUtils::ConvertHRGNToRegion(HRGN aRgn
) {
1645 NS_ASSERTION(aRgn
, "Don't pass NULL region here");
1647 LayoutDeviceIntRegion rgn
;
1649 DWORD size
= ::GetRegionData(aRgn
, 0, nullptr);
1650 AutoTArray
<uint8_t, 100> buffer
;
1651 buffer
.SetLength(size
);
1653 RGNDATA
* data
= reinterpret_cast<RGNDATA
*>(buffer
.Elements());
1654 if (!::GetRegionData(aRgn
, size
, data
)) return rgn
;
1656 if (data
->rdh
.nCount
> MAX_RECTS_IN_REGION
) {
1657 rgn
= ToIntRect(data
->rdh
.rcBound
);
1661 RECT
* rects
= reinterpret_cast<RECT
*>(data
->Buffer
);
1662 for (uint32_t i
= 0; i
< data
->rdh
.nCount
; ++i
) {
1663 RECT
* r
= rects
+ i
;
1664 rgn
.Or(rgn
, ToIntRect(*r
));
1670 LayoutDeviceIntRect
WinUtils::ToIntRect(const RECT
& aRect
) {
1671 return LayoutDeviceIntRect(aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
1672 aRect
.bottom
- aRect
.top
);
1676 bool WinUtils::IsIMEEnabled(const InputContext
& aInputContext
) {
1677 return IsIMEEnabled(aInputContext
.mIMEState
.mEnabled
);
1681 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState
) {
1682 return aIMEState
== IMEEnabled::Enabled
;
1686 void WinUtils::SetupKeyModifiersSequence(nsTArray
<KeyPair
>* aArray
,
1687 uint32_t aModifiers
, UINT aMessage
) {
1688 MOZ_ASSERT(!(aModifiers
& nsIWidget::ALTGRAPH
) ||
1689 !(aModifiers
& (nsIWidget::CTRL_L
| nsIWidget::ALT_R
)));
1690 if (aMessage
== WM_KEYUP
) {
1691 // If AltGr is released, ControlLeft key is released first, then,
1692 // AltRight key is released.
1693 if (aModifiers
& nsIWidget::ALTGRAPH
) {
1694 aArray
->AppendElement(
1695 KeyPair(VK_CONTROL
, VK_LCONTROL
, ScanCode::eControlLeft
));
1696 aArray
->AppendElement(KeyPair(VK_MENU
, VK_RMENU
, ScanCode::eAltRight
));
1698 for (uint32_t i
= ArrayLength(sModifierKeyMap
); i
; --i
) {
1699 const uint32_t* map
= sModifierKeyMap
[i
- 1];
1700 if (aModifiers
& map
[0]) {
1701 aArray
->AppendElement(KeyPair(map
[1], map
[2], map
[3]));
1705 for (uint32_t i
= 0; i
< ArrayLength(sModifierKeyMap
); ++i
) {
1706 const uint32_t* map
= sModifierKeyMap
[i
];
1707 if (aModifiers
& map
[0]) {
1708 aArray
->AppendElement(KeyPair(map
[1], map
[2], map
[3]));
1711 // If AltGr is pressed, ControlLeft key is pressed first, then,
1712 // AltRight key is pressed.
1713 if (aModifiers
& nsIWidget::ALTGRAPH
) {
1714 aArray
->AppendElement(
1715 KeyPair(VK_CONTROL
, VK_LCONTROL
, ScanCode::eControlLeft
));
1716 aArray
->AppendElement(KeyPair(VK_MENU
, VK_RMENU
, ScanCode::eAltRight
));
1722 nsresult
WinUtils::WriteBitmap(nsIFile
* aFile
, imgIContainer
* aImage
) {
1723 RefPtr
<SourceSurface
> surface
= aImage
->GetFrame(
1724 imgIContainer::FRAME_FIRST
,
1725 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
1726 NS_ENSURE_TRUE(surface
, NS_ERROR_FAILURE
);
1728 return WriteBitmap(aFile
, surface
);
1732 nsresult
WinUtils::WriteBitmap(nsIFile
* aFile
, SourceSurface
* surface
) {
1735 // For either of the following formats we want to set the biBitCount member
1736 // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
1737 // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
1738 // for the BI_RGB value we use for the biCompression member.
1739 MOZ_ASSERT(surface
->GetFormat() == SurfaceFormat::B8G8R8A8
||
1740 surface
->GetFormat() == SurfaceFormat::B8G8R8X8
);
1742 RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface();
1743 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
1745 int32_t width
= dataSurface
->GetSize().width
;
1746 int32_t height
= dataSurface
->GetSize().height
;
1747 int32_t bytesPerPixel
= 4 * sizeof(uint8_t);
1748 uint32_t bytesPerRow
= bytesPerPixel
* width
;
1749 bool hasAlpha
= surface
->GetFormat() == SurfaceFormat::B8G8R8A8
;
1751 // initialize these bitmap structs which we will later
1752 // serialize directly to the head of the bitmap file
1754 memset(&bmi
, 0, sizeof(BITMAPV4HEADER
));
1755 bmi
.bV4Size
= sizeof(BITMAPV4HEADER
);
1756 bmi
.bV4Width
= width
;
1757 bmi
.bV4Height
= height
;
1759 bmi
.bV4BitCount
= (WORD
)bytesPerPixel
* 8;
1760 bmi
.bV4V4Compression
= hasAlpha
? BI_BITFIELDS
: BI_RGB
;
1761 bmi
.bV4SizeImage
= bytesPerRow
* height
;
1762 bmi
.bV4CSType
= LCS_sRGB
;
1764 bmi
.bV4RedMask
= 0x00FF0000;
1765 bmi
.bV4GreenMask
= 0x0000FF00;
1766 bmi
.bV4BlueMask
= 0x000000FF;
1767 bmi
.bV4AlphaMask
= 0xFF000000;
1770 BITMAPFILEHEADER bf
;
1772 bf
.bfType
= 0x4D42; // 'BM'
1775 bf
.bfOffBits
= sizeof(BITMAPFILEHEADER
) + sizeof(BITMAPV4HEADER
) +
1776 (hasAlpha
? sizeof(colormask
) : 0);
1777 bf
.bfSize
= bf
.bfOffBits
+ bmi
.bV4SizeImage
;
1779 // get a file output stream
1780 nsCOMPtr
<nsIOutputStream
> stream
;
1781 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(stream
), aFile
);
1782 NS_ENSURE_SUCCESS(rv
, rv
);
1784 DataSourceSurface::MappedSurface map
;
1785 if (!dataSurface
->Map(DataSourceSurface::MapType::READ
, &map
)) {
1786 return NS_ERROR_FAILURE
;
1789 // write the bitmap headers and rgb pixel data to the file
1790 rv
= NS_ERROR_FAILURE
;
1793 stream
->Write((const char*)&bf
, sizeof(BITMAPFILEHEADER
), &written
);
1794 if (written
== sizeof(BITMAPFILEHEADER
)) {
1795 stream
->Write((const char*)&bmi
, sizeof(BITMAPV4HEADER
), &written
);
1796 if (written
== sizeof(BITMAPV4HEADER
)) {
1799 colormask
[0] = 0x00FF0000;
1800 colormask
[1] = 0x0000FF00;
1801 colormask
[2] = 0x000000FF;
1803 stream
->Write((const char*)colormask
, sizeof(colormask
), &written
);
1805 if (!hasAlpha
|| written
== sizeof(colormask
)) {
1806 // write out the image data backwards because the desktop won't
1807 // show bitmaps with negative heights for top-to-bottom
1808 uint32_t i
= map
.mStride
* height
;
1811 stream
->Write(((const char*)map
.mData
) + i
, bytesPerRow
, &written
);
1812 if (written
== bytesPerRow
) {
1815 rv
= NS_ERROR_FAILURE
;
1826 dataSurface
->Unmap();
1831 // This is in use here and in dom/events/TouchEvent.cpp
1833 uint32_t WinUtils::IsTouchDeviceSupportPresent() {
1834 int32_t touchCapabilities
= ::GetSystemMetrics(SM_DIGITIZER
);
1835 return (touchCapabilities
& NID_READY
) &&
1836 (touchCapabilities
& (NID_EXTERNAL_TOUCH
| NID_INTEGRATED_TOUCH
));
1840 uint32_t WinUtils::GetMaxTouchPoints() {
1841 if (IsTouchDeviceSupportPresent()) {
1842 return GetSystemMetrics(SM_MAXIMUMTOUCHES
);
1849 WinUtils::GetPowerPlatformRole() {
1850 typedef POWER_PLATFORM_ROLE(WINAPI
*
1851 PowerDeterminePlatformRoleEx
)(ULONG Version
);
1852 static PowerDeterminePlatformRoleEx power_determine_platform_role
=
1853 reinterpret_cast<PowerDeterminePlatformRoleEx
>(::GetProcAddress(
1854 ::LoadLibraryW(L
"PowrProf.dll"), "PowerDeterminePlatformRoleEx"));
1856 POWER_PLATFORM_ROLE powerPlatformRole
= PlatformRoleUnspecified
;
1857 if (!power_determine_platform_role
) {
1858 return powerPlatformRole
;
1861 return power_determine_platform_role(POWER_PLATFORM_ROLE_V2
);
1864 static bool CallGetAutoRotationState(AR_STATE
* aRotationState
) {
1865 typedef BOOL(WINAPI
* GetAutoRotationStateFunc
)(PAR_STATE pState
);
1866 static GetAutoRotationStateFunc get_auto_rotation_state_func
=
1867 reinterpret_cast<GetAutoRotationStateFunc
>(::GetProcAddress(
1868 GetModuleHandleW(L
"user32.dll"), "GetAutoRotationState"));
1869 if (get_auto_rotation_state_func
) {
1870 ZeroMemory(aRotationState
, sizeof(AR_STATE
));
1871 return get_auto_rotation_state_func(aRotationState
);
1876 static bool IsTabletDevice() {
1878 // - The device has a touch screen.
1879 // - It is used as a tablet which means that it has no keyboard connected.
1880 // On Windows 10 it means that it is verifying with ConvertibleSlateMode.
1882 if (!IsWin8OrLater()) {
1886 if (WindowsUIUtils::GetInTabletMode()) {
1890 if (!GetSystemMetrics(SM_MAXIMUMTOUCHES
)) {
1894 // If the device is docked, the user is treating the device as a PC.
1895 if (GetSystemMetrics(SM_SYSTEMDOCKED
)) {
1899 // If the device is not supporting rotation, it's unlikely to be a tablet,
1900 // a convertible or a detachable. See:
1901 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
1902 AR_STATE rotation_state
;
1903 if (CallGetAutoRotationState(&rotation_state
) &&
1904 (rotation_state
& (AR_NOT_SUPPORTED
| AR_LAPTOP
| AR_NOSENSOR
))) {
1908 // PlatformRoleSlate was added in Windows 8+.
1909 POWER_PLATFORM_ROLE role
= WinUtils::GetPowerPlatformRole();
1910 if (role
== PlatformRoleMobile
|| role
== PlatformRoleSlate
) {
1911 return !GetSystemMetrics(SM_CONVERTIBLESLATEMODE
);
1916 static bool IsMousePresent() {
1917 if (!::GetSystemMetrics(SM_MOUSEPRESENT
)) {
1921 DWORD count
= InputDeviceUtils::CountMouseDevices();
1926 // If there is a mouse device and if this machine is a tablet or has a
1927 // digitizer, that's counted as the mouse device.
1928 // FIXME: Bug 1495938: We should drop this heuristic way once we find out a
1929 // reliable way to tell there is no mouse or not.
1931 (WinUtils::IsTouchDeviceSupportPresent() || IsTabletDevice())) {
1939 PointerCapabilities
WinUtils::GetPrimaryPointerCapabilities() {
1940 if (IsTabletDevice()) {
1941 return PointerCapabilities::Coarse
;
1944 if (IsMousePresent()) {
1945 return PointerCapabilities::Fine
| PointerCapabilities::Hover
;
1948 if (IsTouchDeviceSupportPresent()) {
1949 return PointerCapabilities::Coarse
;
1952 return PointerCapabilities::None
;
1956 PointerCapabilities
WinUtils::GetAllPointerCapabilities() {
1957 PointerCapabilities result
= PointerCapabilities::None
;
1959 if (IsTabletDevice() || IsTouchDeviceSupportPresent()) {
1960 result
|= PointerCapabilities::Coarse
;
1963 if (IsMousePresent()) {
1964 result
|= PointerCapabilities::Fine
| PointerCapabilities::Hover
;
1971 bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring
& aPath
) {
1972 LOG_D("ResolveJunctionPointsAndSymLinks: Resolving path: %S", aPath
.c_str());
1974 wchar_t path
[MAX_PATH
] = {0};
1976 nsAutoHandle
handle(::CreateFileW(
1977 aPath
.c_str(), 0, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1978 nullptr, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, nullptr));
1980 if (handle
== INVALID_HANDLE_VALUE
) {
1981 LOG_E("Failed to open file handle to resolve path. GetLastError=%d",
1986 DWORD pathLen
= GetFinalPathNameByHandleW(
1987 handle
, path
, MAX_PATH
, FILE_NAME_NORMALIZED
| VOLUME_NAME_DOS
);
1988 if (pathLen
== 0 || pathLen
>= MAX_PATH
) {
1989 LOG_E("GetFinalPathNameByHandleW failed. GetLastError=%d", GetLastError());
1994 // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
1995 // but that confuses some APIs so strip it off. It will also put
1996 // '\\?\UNC\' in front of network paths, we convert that to '\\'.
1997 if (aPath
.compare(0, 7, L
"\\\\?\\UNC") == 0) {
1999 } else if (aPath
.compare(0, 4, L
"\\\\?\\") == 0) {
2003 LOG_D("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
2009 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile
* aPath
) {
2012 nsAutoString filePath
;
2013 nsresult rv
= aPath
->GetPath(filePath
);
2014 if (NS_WARN_IF(NS_FAILED(rv
))) {
2018 std::wstring
resolvedPath(filePath
.get());
2019 if (!ResolveJunctionPointsAndSymLinks(resolvedPath
)) {
2023 rv
= aPath
->InitWithPath(nsDependentString(resolvedPath
.c_str()));
2024 if (NS_WARN_IF(NS_FAILED(rv
))) {
2032 bool WinUtils::RunningFromANetworkDrive() {
2033 wchar_t exePath
[MAX_PATH
];
2034 if (!::GetModuleFileNameW(nullptr, exePath
, MAX_PATH
)) {
2038 std::wstring
exeString(exePath
);
2039 if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString
)) {
2043 wchar_t volPath
[MAX_PATH
];
2044 if (!::GetVolumePathNameW(exeString
.c_str(), volPath
, MAX_PATH
)) {
2048 return (::GetDriveTypeW(volPath
) == DRIVE_REMOTE
);
2052 bool WinUtils::CanonicalizePath(nsAString
& aPath
) {
2053 wchar_t tempPath
[MAX_PATH
+ 1];
2054 if (!PathCanonicalizeW(tempPath
,
2055 (char16ptr_t
)PromiseFlatString(aPath
).get())) {
2059 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
2064 bool WinUtils::MakeLongPath(nsAString
& aPath
) {
2065 wchar_t tempPath
[MAX_PATH
+ 1];
2067 GetLongPathNameW((char16ptr_t
)PromiseFlatString(aPath
).get(), tempPath
,
2068 ArrayLength(tempPath
));
2069 if (longResult
> ArrayLength(tempPath
)) {
2070 // Our buffer is too short, and we're guaranteeing <= MAX_PATH results.
2072 } else if (longResult
) {
2075 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
2077 // GetLongPathNameW returns 0 if the path is not found or is not rooted,
2078 // but we shouldn't consider that a failure condition.
2083 bool WinUtils::UnexpandEnvVars(nsAString
& aPath
) {
2084 wchar_t tempPath
[MAX_PATH
+ 1];
2085 // PathUnExpandEnvStringsW returns false if it doesn't make any
2086 // substitutions. Silently continue using the unaltered path.
2087 if (PathUnExpandEnvStringsW((char16ptr_t
)PromiseFlatString(aPath
).get(),
2088 tempPath
, ArrayLength(tempPath
))) {
2090 MOZ_ASSERT(aPath
.Length() <= MAX_PATH
);
2096 WinUtils::WhitelistVec
WinUtils::BuildWhitelist() {
2097 WhitelistVec result
;
2099 Unused
<< result
.emplaceBack(
2100 std::make_pair(nsString(u
"%ProgramFiles%"_ns
), nsDependentString()));
2102 // When no substitution is required, set the void flag
2103 result
.back().second
.SetIsVoid(true);
2105 Unused
<< result
.emplaceBack(
2106 std::make_pair(nsString(u
"%SystemRoot%"_ns
), nsDependentString()));
2107 result
.back().second
.SetIsVoid(true);
2109 wchar_t tmpPath
[MAX_PATH
+ 1] = {};
2110 if (GetTempPath(MAX_PATH
, tmpPath
)) {
2111 // GetTempPath's result always ends with a backslash, which we don't want
2112 uint32_t tmpPathLen
= wcslen(tmpPath
);
2114 tmpPath
[tmpPathLen
- 1] = 0;
2117 nsAutoString
cleanTmpPath(tmpPath
);
2118 if (UnexpandEnvVars(cleanTmpPath
)) {
2119 constexpr auto tempVar
= u
"%TEMP%"_ns
;
2120 Unused
<< result
.emplaceBack(std::make_pair(
2121 nsString(cleanTmpPath
), nsDependentString(tempVar
, 0)));
2125 // If we add more items to the whitelist, ensure we still don't invoke an
2126 // unnecessary heap allocation.
2127 MOZ_ASSERT(result
.length() <= kMaxWhitelistedItems
);
2133 * This function provides an array of (system path, substitution) pairs that are
2134 * considered to be acceptable with respect to privacy, for the purposes of
2135 * submitting within telemetry or crash reports.
2137 * The substitution string's void flag may be set. If it is, no subsitution is
2138 * necessary. Otherwise, the consumer should replace the system path with the
2141 * @see PreparePathForTelemetry for an example of its usage.
2144 const WinUtils::WhitelistVec
& WinUtils::GetWhitelistedPaths() {
2145 static WhitelistVec
sWhitelist([]() -> WhitelistVec
{
2146 auto setClearFn
= [ptr
= &sWhitelist
]() -> void {
2147 RunOnShutdown([ptr
]() -> void { ptr
->clear(); },
2148 ShutdownPhase::XPCOMShutdownFinal
);
2151 if (NS_IsMainThread()) {
2154 SchedulerGroup::Dispatch(
2155 TaskCategory::Other
,
2156 NS_NewRunnableFunction("WinUtils::GetWhitelistedPaths",
2157 std::move(setClearFn
)));
2160 return BuildWhitelist();
2166 * This function is located here (as opposed to nsSystemInfo or elsewhere)
2167 * because we need to gather this information as early as possible during
2171 bool WinUtils::GetAppInitDLLs(nsAString
& aOutput
) {
2174 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
2175 L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
2176 0, KEY_QUERY_VALUE
, &hkey
)) {
2179 nsAutoRegKey
key(hkey
);
2181 const wchar_t kLoadAppInitDLLs
[] = L
"LoadAppInit_DLLs";
2182 DWORD loadAppInitDLLs
= 0;
2183 DWORD loadAppInitDLLsLen
= sizeof(loadAppInitDLLs
);
2184 status
= RegQueryValueExW(hkey
, kLoadAppInitDLLs
, nullptr, nullptr,
2185 (LPBYTE
)&loadAppInitDLLs
, &loadAppInitDLLsLen
);
2186 if (status
!= ERROR_SUCCESS
) {
2189 if (!loadAppInitDLLs
) {
2190 // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
2191 // In this case we'll return true along with an empty output string.
2195 const wchar_t kAppInitDLLs
[] = L
"AppInit_DLLs";
2196 // Query for required buffer size
2197 status
= RegQueryValueExW(hkey
, kAppInitDLLs
, nullptr, nullptr, nullptr,
2199 if (status
!= ERROR_SUCCESS
) {
2202 // Allocate the buffer and query for the actual data
2203 mozilla::UniquePtr
<wchar_t[]> data
=
2204 mozilla::MakeUnique
<wchar_t[]>(numBytes
/ sizeof(wchar_t));
2205 status
= RegQueryValueExW(hkey
, kAppInitDLLs
, nullptr, nullptr,
2206 (LPBYTE
)data
.get(), &numBytes
);
2207 if (status
!= ERROR_SUCCESS
) {
2210 // For each token, split up the filename components and then check the
2211 // name of the file.
2212 const wchar_t kDelimiters
[] = L
", ";
2213 wchar_t* tokenContext
= nullptr;
2214 wchar_t* token
= wcstok_s(data
.get(), kDelimiters
, &tokenContext
);
2216 nsAutoString
cleanPath(token
);
2217 // Since these paths are short paths originating from the registry, we need
2218 // to canonicalize them, lengthen them, and sanitize them before we can
2219 // check them against the whitelist
2220 if (PreparePathForTelemetry(cleanPath
)) {
2221 if (!aOutput
.IsEmpty()) {
2224 aOutput
+= cleanPath
;
2226 token
= wcstok_s(nullptr, kDelimiters
, &tokenContext
);
2232 bool WinUtils::PreparePathForTelemetry(nsAString
& aPath
,
2233 PathTransformFlags aFlags
) {
2234 if (aFlags
& PathTransformFlags::Canonicalize
) {
2235 if (!CanonicalizePath(aPath
)) {
2239 if (aFlags
& PathTransformFlags::Lengthen
) {
2240 if (!MakeLongPath(aPath
)) {
2244 if (aFlags
& PathTransformFlags::UnexpandEnvVars
) {
2245 if (!UnexpandEnvVars(aPath
)) {
2250 const WhitelistVec
& whitelistedPaths
= GetWhitelistedPaths();
2252 for (uint32_t i
= 0; i
< whitelistedPaths
.length(); ++i
) {
2253 const nsString
& testPath
= whitelistedPaths
[i
].first
;
2254 const nsDependentString
& substitution
= whitelistedPaths
[i
].second
;
2255 if (StringBeginsWith(aPath
, testPath
, nsCaseInsensitiveStringComparator
)) {
2256 if (!substitution
.IsVoid()) {
2257 aPath
.Replace(0, testPath
.Length(), substitution
);
2263 // For non-whitelisted paths, we strip the path component and just leave
2264 // the filename. We can't use nsLocalFile to do this because these paths may
2265 // begin with environment variables, and nsLocalFile doesn't like
2266 // non-absolute paths.
2267 const nsString
& flatPath
= PromiseFlatString(aPath
);
2268 LPCWSTR leafStart
= ::PathFindFileNameW(flatPath
.get());
2269 ptrdiff_t cutLen
= leafStart
- flatPath
.get();
2271 aPath
.Cut(0, cutLen
);
2272 } else if (aFlags
& PathTransformFlags::RequireFilePath
) {
2279 nsString
WinUtils::GetPackageFamilyName() {
2282 UniquePtr
<wchar_t[]> packageIdentity
= mozilla::GetPackageFamilyName();
2283 if (packageIdentity
) {
2284 rv
= packageIdentity
.get();
2290 bool WinUtils::GetClassName(HWND aHwnd
, nsAString
& aClassName
) {
2291 const int bufferLength
= 256;
2292 aClassName
.SetLength(bufferLength
);
2294 int length
= ::GetClassNameW(aHwnd
, (char16ptr_t
)aClassName
.BeginWriting(),
2299 MOZ_RELEASE_ASSERT(length
<= (bufferLength
- 1));
2300 aClassName
.Truncate(length
);
2304 static BOOL CALLBACK
EnumUpdateWindowOcclusionProc(HWND aHwnd
, LPARAM aLParam
) {
2305 const bool* const enable
= reinterpret_cast<bool*>(aLParam
);
2306 nsWindow
* window
= WinUtils::GetNSWindowPtr(aHwnd
);
2308 window
->MaybeEnableWindowOcclusion(*enable
);
2313 void WinUtils::EnableWindowOcclusion(const bool aEnable
) {
2315 WinWindowOcclusionTracker::Ensure();
2317 ::EnumWindows(EnumUpdateWindowOcclusionProc
,
2318 reinterpret_cast<LPARAM
>(&aEnable
));
2321 void WinUtils::GetDisplayOrientation(const char16ptr_t aName
,
2322 hal::ScreenOrientation
& aOrientation
,
2324 aOrientation
= hal::ScreenOrientation::None
;
2327 DEVMODEW mode
= {.dmSize
= sizeof(DEVMODEW
)};
2328 if (!EnumDisplaySettingsW(aName
, ENUM_CURRENT_SETTINGS
, &mode
)) {
2331 MOZ_ASSERT(mode
.dmFields
& DM_DISPLAYORIENTATION
);
2333 // conver to default/natural size
2334 if (mode
.dmDisplayOrientation
== DMDO_90
||
2335 mode
.dmDisplayOrientation
== DMDO_270
) {
2336 DWORD temp
= mode
.dmPelsHeight
;
2337 mode
.dmPelsHeight
= mode
.dmPelsWidth
;
2338 mode
.dmPelsWidth
= temp
;
2341 bool defaultIsLandscape
= mode
.dmPelsWidth
>= mode
.dmPelsHeight
;
2342 switch (mode
.dmDisplayOrientation
) {
2344 aOrientation
= defaultIsLandscape
2345 ? hal::ScreenOrientation::LandscapePrimary
2346 : hal::ScreenOrientation::PortraitPrimary
;
2350 aOrientation
= defaultIsLandscape
2351 ? hal::ScreenOrientation::PortraitPrimary
2352 : hal::ScreenOrientation::LandscapeSecondary
;
2356 aOrientation
= defaultIsLandscape
2357 ? hal::ScreenOrientation::LandscapeSecondary
2358 : hal::ScreenOrientation::PortraitSecondary
;
2362 aOrientation
= defaultIsLandscape
2363 ? hal::ScreenOrientation::PortraitSecondary
2364 : hal::ScreenOrientation::LandscapePrimary
;
2368 MOZ_ASSERT_UNREACHABLE("Unexpected angle");
2374 } // namespace widget
2375 } // namespace mozilla