/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_widget_WinUtils_h__ #define mozilla_widget_WinUtils_h__ #include "nscore.h" #include #include #include #include #include #include // Undo the windows.h damage #undef GetMessage #undef CreateEvent #undef GetClassName #undef GetBinaryType #undef RemoveDirectory #include "nsString.h" #include "nsRegion.h" #include "nsRect.h" #include "nsIRunnable.h" #include "nsICryptoHash.h" #ifdef MOZ_PLACES # include "nsIFaviconService.h" #endif #include "nsIDownloader.h" #include "nsIURI.h" #include "nsIWidget.h" #include "nsIThread.h" #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/HalScreenConfiguration.h" #include "mozilla/HashTable.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" #include "mozilla/WindowsDpiAwareness.h" #include "mozilla/WindowsProcessMitigations.h" #include "mozilla/gfx/2D.h" // Starting with version 10.0.22621.0 of the Windows SDK the AR_STATE enum and // types are only defined when building for Windows 8 instead of Windows 7 // (although they are always defined for MinGW) #if (WDK_NTDDI_VERSION >= 0x0A00000C) && (WINVER < 0x0602) && \ (!defined(__MINGW32__)) enum tagAR_STATE { AR_ENABLED = 0x0, AR_DISABLED = 0x1, AR_SUPPRESSED = 0x2, AR_REMOTESESSION = 0x4, AR_MULTIMON = 0x8, AR_NOSENSOR = 0x10, AR_NOT_SUPPORTED = 0x20, AR_DOCKED = 0x40, AR_LAPTOP = 0x80 }; typedef enum tagAR_STATE AR_STATE; using PAR_STATE = enum tagAR_STATE*; #endif // (WDK_NTDDI_VERSION >= 0x0A00000C) && (WINVER < 0x0602) /** * NS_INLINE_DECL_IUNKNOWN_REFCOUNTING should be used for defining and * implementing AddRef() and Release() of IUnknown interface. * This depends on xpcom/base/nsISupportsImpl.h. */ #define NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(_class) \ public: \ STDMETHODIMP_(ULONG) AddRef() { \ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ NS_ASSERT_OWNINGTHREAD(_class); \ ++mRefCnt; \ NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \ return static_cast(mRefCnt.get()); \ } \ STDMETHODIMP_(ULONG) Release() { \ MOZ_ASSERT(int32_t(mRefCnt) > 0, \ "Release called on object that has already been released!"); \ NS_ASSERT_OWNINGTHREAD(_class); \ --mRefCnt; \ NS_LOG_RELEASE(this, mRefCnt, #_class); \ if (mRefCnt == 0) { \ NS_ASSERT_OWNINGTHREAD(_class); \ mRefCnt = 1; /* stabilize */ \ delete this; \ return 0; \ } \ return static_cast(mRefCnt.get()); \ } \ \ protected: \ nsAutoRefCnt mRefCnt; \ NS_DECL_OWNINGTHREAD \ public: class nsWindow; struct KeyPair; namespace mozilla { enum class PointerCapabilities : uint8_t; #if defined(ACCESSIBILITY) namespace a11y { class LocalAccessible; } // namespace a11y #endif // defined(ACCESSIBILITY) // Helper function: enumerate all the toplevel HWNDs attached to the current // thread via ::EnumThreadWindows(). // // Note that this use of ::EnumThreadWindows() is, unfortunately, not an // abstract implementation detail. template void EnumerateThreadWindows(F&& f) // requires requires(F f, HWND h) { f(h); } { class Impl { public: F f; explicit Impl(F&& f) : f(std::forward(f)) {} void invoke() { WNDENUMPROC proc = &Impl::Callback; ::EnumThreadWindows(::GetCurrentThreadId(), proc, reinterpret_cast(&f)); } private: static BOOL CALLBACK Callback(HWND hwnd, LPARAM lp) { (*reinterpret_cast(lp))(hwnd); return TRUE; } }; Impl(std::forward(f)).invoke(); } namespace widget { // More complete QS definitions for MsgWaitForMultipleObjects() and // GetQueueStatus() that include newer win8 specific defines. #ifndef QS_RAWINPUT # define QS_RAWINPUT 0x0400 #endif #ifndef QS_TOUCH # define QS_TOUCH 0x0800 # define QS_POINTER 0x1000 #endif #define MOZ_QS_ALLEVENT \ (QS_KEY | QS_MOUSEMOVE | QS_MOUSEBUTTON | QS_POSTMESSAGE | QS_TIMER | \ QS_PAINT | QS_SENDMESSAGE | QS_HOTKEY | QS_ALLPOSTMESSAGE | QS_RAWINPUT | \ QS_TOUCH | QS_POINTER) // Logging macros #define LogFunction() mozilla::widget::WinUtils::Log(__FUNCTION__) #define LogThread() \ mozilla::widget::WinUtils::Log("%s: IsMainThread:%d ThreadId:%X", \ __FUNCTION__, NS_IsMainThread(), \ GetCurrentThreadId()) #define LogThis() mozilla::widget::WinUtils::Log("[%X] %s", this, __FUNCTION__) #define LogException(e) \ mozilla::widget::WinUtils::Log("%s Exception:%s", __FUNCTION__, \ e->ToString()->Data()) #define LogHRESULT(hr) \ mozilla::widget::WinUtils::Log("%s hr=%X", __FUNCTION__, hr) #ifdef MOZ_PLACES class myDownloadObserver final : public nsIDownloadObserver { ~myDownloadObserver() {} public: NS_DECL_ISUPPORTS NS_DECL_NSIDOWNLOADOBSERVER }; #endif class WinUtils { // Function pointers for APIs that may not be available depending on // the Win10 update version -- will be set up in Initialize(). static SetThreadDpiAwarenessContextProc sSetThreadDpiAwarenessContext; static EnableNonClientDpiScalingProc sEnableNonClientDpiScaling; static GetSystemMetricsForDpiProc sGetSystemMetricsForDpi; // Set on Initialize(). static bool sHasPackageIdentity; public: class AutoSystemDpiAware { public: AutoSystemDpiAware() { MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown()); if (sSetThreadDpiAwarenessContext) { mPrevContext = sSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); } } ~AutoSystemDpiAware() { if (sSetThreadDpiAwarenessContext) { sSetThreadDpiAwarenessContext(mPrevContext); } } private: DPI_AWARENESS_CONTEXT mPrevContext; }; // Wrapper for DefWindowProc that will enable non-client dpi scaling on the // window during creation. static LRESULT WINAPI NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); /** * Get the system's default logical-to-physical DPI scaling factor, * which is based on the primary display. Note however that unlike * LogToPhysFactor(GetPrimaryMonitor()), this will not change during * a session even if the displays are reconfigured. This scale factor * is used by Windows theme metrics etc, which do not fully support * dynamic resolution changes but are only updated on logout. */ static double SystemScaleFactor(); static bool IsPerMonitorDPIAware(); /** * Get the DPI of the given monitor if it's per-monitor DPI aware, otherwise * return the system DPI. */ static float MonitorDPI(HMONITOR aMonitor); static float SystemDPI(); /** * Functions to convert between logical pixels as used by most Windows APIs * and physical (device) pixels. */ static double LogToPhysFactor(HMONITOR aMonitor); static double LogToPhysFactor(HWND aWnd); static double LogToPhysFactor(HDC aDC) { return LogToPhysFactor(::WindowFromDC(aDC)); } static int32_t LogToPhys(HMONITOR aMonitor, double aValue); static HMONITOR GetPrimaryMonitor(); static HMONITOR MonitorFromRect(const gfx::Rect& rect); static bool HasSystemMetricsForDpi(); static int GetSystemMetricsForDpi(int nIndex, UINT dpi); /** * @param msg Windows event message * @return User-friendly event name, or nullptr if no * match is found. */ static const char* WinEventToEventName(UINT msg); /** * @param aHdc HDC for printer * @return unwritable margins for currently set page on aHdc or empty margins * if aHdc is null */ static gfx::MarginDouble GetUnwriteableMarginsForDeviceInInches(HDC aHdc); static bool HasPackageIdentity() { return sHasPackageIdentity; } /* * The "family name" of a Windows app package is the full name without any of * the components that might change during the life cycle of the app (such as * the version number, or the architecture). This leaves only those properties * which together serve to uniquely identify the app within one Windows * installation, namely the base name and the publisher name. Meaning, this * string is safe to use anywhere that a string uniquely identifying an app * installation is called for (because multiple copies of the same app on the * same system is not a supported feature in the app framework). */ static nsString GetPackageFamilyName(); /** * Logging helpers that dump output to prlog module 'Widget', console, and * OutputDebugString. Note these output in both debug and release builds. */ static void Log(const char* fmt, ...); static void LogW(const wchar_t* fmt, ...); /** * PeekMessage() and GetMessage() are wrapper methods for PeekMessageW(), * GetMessageW(), ITfMessageMgr::PeekMessageW() and * ITfMessageMgr::GetMessageW(). * Don't call the native APIs directly. You MUST use these methods instead. */ static bool PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, UINT aLastMessage, UINT aOption); static bool GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, UINT aLastMessage); /** * Wait until a message is ready to be processed. * Prefer using this method to directly calling ::WaitMessage since * ::WaitMessage will wait if there is an unread message in the queue. * That can cause freezes until another message enters the queue if the * message is marked read by a call to PeekMessage which the caller is * not aware of (e.g., from a different thread). * Note that this method may cause sync dispatch of sent (as opposed to * posted) messages. * @param aTimeoutMs Timeout for waiting in ms, defaults to INFINITE */ static void WaitForMessage(DWORD aTimeoutMs = INFINITE); /** * Gets the value of a string-typed registry value. * * @param aRoot The registry root to search in. * @param aKeyName The name of the registry key to open. * @param aValueName The name of the registry value in the specified key whose * value is to be retrieved. Can be null, to retrieve the key's unnamed/ * default value. * @param aBuffer The buffer into which to store the string value. Can be * null, in which case the return value indicates just whether the value * exists. * @param aBufferLength The size of aBuffer, in bytes. * @return Whether the value exists and is a string. */ static bool GetRegistryKey(HKEY aRoot, char16ptr_t aKeyName, char16ptr_t aValueName, wchar_t* aBuffer, DWORD aBufferLength); /** * Checks whether the registry key exists in either 32bit or 64bit branch on * the environment. * * @param aRoot The registry root of aName. * @param aKeyName The name of the registry key to check. * @return TRUE if it exists and is readable. Otherwise, FALSE. */ static bool HasRegistryKey(HKEY aRoot, char16ptr_t aKeyName); /** * GetTopLevelHWND() returns a window handle of the top level window which * aWnd belongs to. Note that the result may not be our window, i.e., it * may not be managed by nsWindow. * * See follwing table for the detail of the result window type. * * +-------------------------+-----------------------------------------------+ * | | aStopIfNotPopup | * +-------------------------+-----------------------+-----------------------+ * | | TRUE | FALSE | + +-----------------+-------+-----------------------+-----------------------+ * | | | * an independent top level window | * | | TRUE | * a pupup window (WS_POPUP) | * | | | * an owned top level window (like dialog) | * | aStopIfNotChild +-------+-----------------------+-----------------------+ * | | | * independent window | * only an independent | * | | FALSE | * non-popup-owned- | top level window | * | | | window like dialog | | * +-----------------+-------+-----------------------+-----------------------+ */ static HWND GetTopLevelHWND(HWND aWnd, bool aStopIfNotChild = false, bool aStopIfNotPopup = true); /** * SetNSWindowPtr() associates aWindow with aWnd. If aWidget is nullptr, it * instead dissociates any nsWindow from aWnd. * * No AddRef is performed. May not be used off of the main thread. */ static void SetNSWindowPtr(HWND aWnd, nsWindow* aWindow); /** * GetNSWindowPtr() returns a pointer to the associated nsWindow pointer, if * one exists, or nullptr, if not. * * No AddRef is performed. May not be used off of the main thread. */ static nsWindow* GetNSWindowPtr(HWND aWnd); /** * GetMonitorCount() returns count of monitors on the environment. */ static int32_t GetMonitorCount(); /** * IsOurProcessWindow() returns TRUE if aWnd belongs our process. * Otherwise, FALSE. */ static bool IsOurProcessWindow(HWND aWnd); /** * FindOurProcessWindow() returns the nearest ancestor window which * belongs to our process. If it fails to find our process's window by the * top level window, returns nullptr. And note that this is using * ::GetParent() for climbing the window hierarchy, therefore, it gives * up at an owned top level window except popup window (e.g., dialog). */ static HWND FindOurProcessWindow(HWND aWnd); /** * FindOurWindowAtPoint() returns the topmost child window which belongs to * our process's top level window. * * NOTE: the topmost child window may NOT be our process's window like a * plugin's window. */ static HWND FindOurWindowAtPoint(const POINT& aPointInScreen); /** * InitMSG() returns an MSG struct which was initialized by the params. * Don't trust the other members in the result. */ static MSG InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd); /** * GetScanCode() returns a scan code for the LPARAM of WM_KEYDOWN, WM_KEYUP, * WM_CHAR and WM_UNICHAR. * */ static WORD GetScanCode(LPARAM aLParam) { return (aLParam >> 16) & 0xFF; } /** * IsExtendedScanCode() returns TRUE if the LPARAM indicates the key message * is an extended key event. */ static bool IsExtendedScanCode(LPARAM aLParam) { return (aLParam & 0x1000000) != 0; } /** * GetInternalMessage() converts a native message to an internal message. * If there is no internal message for the given native message, returns * the native message itself. */ static UINT GetInternalMessage(UINT aNativeMessage); /** * GetNativeMessage() converts an internal message to a native message. * If aInternalMessage is a native message, returns the native message itself. */ static UINT GetNativeMessage(UINT aInternalMessage); /** * GetMouseInputSource() returns a pointing device information. The value is * one of MouseEvent_Binding::MOZ_SOURCE_*. This method MUST be called during * mouse message handling. */ static uint16_t GetMouseInputSource(); /** * Windows also fires mouse window messages for pens and touches, so we should * retrieve their pointer ID on receiving mouse events as well. Please refer * to * https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx */ static uint16_t GetMousePointerID(); static bool GetIsMouseFromTouch(EventMessage aEventType); /** * ConvertHRGNToRegion converts a Windows HRGN to an LayoutDeviceIntRegion. * * aRgn the HRGN to convert. * returns the LayoutDeviceIntRegion. */ static LayoutDeviceIntRegion ConvertHRGNToRegion(HRGN aRgn); /** * ToIntRect converts a Windows RECT to a LayoutDeviceIntRect. * * aRect the RECT to convert. * returns the LayoutDeviceIntRect. */ static LayoutDeviceIntRect ToIntRect(const RECT& aRect); /** * Returns true if the context or IME state is enabled. Otherwise, false. */ static bool IsIMEEnabled(const InputContext& aInputContext); static bool IsIMEEnabled(IMEEnabled aIMEState); /** * Returns modifier key array for aModifiers. This is for * nsIWidget::SynthethizeNative*Event(). */ static void SetupKeyModifiersSequence(nsTArray* aArray, uint32_t aModifiers, UINT aMessage); /** * Does device have touch support */ static uint32_t IsTouchDeviceSupportPresent(); /** * The maximum number of simultaneous touch contacts supported by the device. * In the case of devices with multiple digitizers (e.g. multiple touch * screens), the value will be the maximum of the set of maximum supported * contacts by each individual digitizer. */ static uint32_t GetMaxTouchPoints(); /** * Returns the windows power platform role, which is useful for detecting * tablets. */ static POWER_PLATFORM_ROLE GetPowerPlatformRole(); // For pointer and hover media queries features. static PointerCapabilities GetPrimaryPointerCapabilities(); // For any-pointer and any-hover media queries features. static PointerCapabilities GetAllPointerCapabilities(); /** * Fully resolves a path to its final path name. So if path contains * junction points or symlinks to other folders, we'll resolve the path * fully to the actual path that the links target. * * @param aPath path to be resolved. * @return true if successful, including if nothing needs to be changed. * false if something failed or aPath does not exist, aPath will * remain unchanged. */ static bool ResolveJunctionPointsAndSymLinks(std::wstring& aPath); static bool ResolveJunctionPointsAndSymLinks(nsIFile* aPath); /** * Returns true if executable's path is on a network drive. */ static bool RunningFromANetworkDrive(); static void Initialize(); static nsresult WriteBitmap(nsIFile* aFile, mozilla::gfx::SourceSurface* surface); // This function is a helper, but it cannot be called from the main thread. // Use the one above! static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage); /** * Wrapper for PathCanonicalize(). * Upon success, the resulting output string length is <= MAX_PATH. * @param aPath [in,out] The path to transform. * @return true on success, false on failure. */ static bool CanonicalizePath(nsAString& aPath); /** * Converts short paths (e.g. "C:\\PROGRA~1\\XYZ") to full paths. * Upon success, the resulting output string length is <= MAX_PATH. * @param aPath [in,out] The path to transform. * @return true on success, false on failure. */ static bool MakeLongPath(nsAString& aPath); /** * Wrapper for PathUnExpandEnvStringsW(). * Upon success, the resulting output string length is <= MAX_PATH. * @param aPath [in,out] The path to transform. * @return true on success, false on failure. */ static bool UnexpandEnvVars(nsAString& aPath); /** * Retrieve a semicolon-delimited list of DLL files derived from AppInit_DLLs */ static bool GetAppInitDLLs(nsAString& aOutput); enum class PathTransformFlags : uint32_t { Canonicalize = 1, Lengthen = 2, UnexpandEnvVars = 4, RequireFilePath = 8, Default = 7, // Default omits RequireFilePath }; /** * Given a path, transforms it in preparation to be reported via telemetry. * That can include canonicalization, converting short to long paths, * unexpanding environment strings, and removing potentially sensitive data * from the path. * * @param aPath [in,out] The path to transform. * @param aFlags [in] Specifies which transformations to perform, allowing * the caller to skip operations they know have already been * performed. * @return true on success, false on failure. */ static bool PreparePathForTelemetry( nsAString& aPath, PathTransformFlags aFlags = PathTransformFlags::Default); static const size_t kMaxWhitelistedItems = 3; using WhitelistVec = Vector, kMaxWhitelistedItems>; static const WhitelistVec& GetWhitelistedPaths(); static bool GetClassName(HWND aHwnd, nsAString& aName); static void EnableWindowOcclusion(const bool aEnable); static bool GetTimezoneName(wchar_t* aBuffer); #ifdef DEBUG static nsresult SetHiDPIMode(bool aHiDPI); static nsresult RestoreHiDPIMode(); #endif static bool GetAutoRotationState(AR_STATE* aRotationState); private: static WhitelistVec BuildWhitelist(); public: #ifdef ACCESSIBILITY static a11y::LocalAccessible* GetRootAccessibleForHWND(HWND aHwnd); #endif }; #ifdef MOZ_PLACES class AsyncFaviconDataReady final : public nsIFaviconDataCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIFAVICONDATACALLBACK AsyncFaviconDataReady(nsIURI* aNewURI, RefPtr& aIOThread, const bool aURLShortcut, already_AddRefed aRunnable); nsresult OnFaviconDataNotAvailable(void); private: ~AsyncFaviconDataReady() {} nsCOMPtr mNewURI; RefPtr mIOThread; nsCOMPtr mRunnable; const bool mURLShortcut; }; #endif /** * Asynchronously tries add the list to the build */ class AsyncEncodeAndWriteIcon : public nsIRunnable { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer // passed in AsyncEncodeAndWriteIcon(const nsAString& aIconPath, UniquePtr aData, uint32_t aStride, uint32_t aWidth, uint32_t aHeight, already_AddRefed aRunnable); private: virtual ~AsyncEncodeAndWriteIcon(); nsAutoString mIconPath; UniquePtr mBuffer; nsCOMPtr mRunnable; uint32_t mStride; uint32_t mWidth; uint32_t mHeight; }; class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE explicit AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent = false); private: virtual ~AsyncDeleteAllFaviconsFromDisk(); int32_t mIcoNoDeleteSeconds; bool mIgnoreRecent; nsCOMPtr mJumpListCacheDir; }; class FaviconHelper { public: static const char kJumpListCacheDir[]; static const char kShortcutCacheDir[]; static nsresult ObtainCachedIconFile( nsCOMPtr aFaviconPageURI, nsString& aICOFilePath, RefPtr& aIOThread, bool aURLShortcut, already_AddRefed aRunnable = nullptr); static nsresult HashURI(nsCOMPtr& aCryptoHash, nsIURI* aUri, nsACString& aUriHash); static nsresult GetOutputIconPath(nsCOMPtr aFaviconPageURI, nsCOMPtr& aICOFile, bool aURLShortcut); static nsresult CacheIconFileFromFaviconURIAsync( nsCOMPtr aFaviconPageURI, nsCOMPtr aICOFile, RefPtr& aIOThread, bool aURLShortcut, already_AddRefed aRunnable); static int32_t GetICOCacheSecondsTimeout(); }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WinUtils::PathTransformFlags); // RTL shim windows are temporary child windows of our nsWindows created to // address RTL issues in picker dialogs. (See bug 588735.) class MOZ_STACK_CLASS ScopedRtlShimWindow { public: explicit ScopedRtlShimWindow(nsIWidget* aParent); ~ScopedRtlShimWindow(); ScopedRtlShimWindow(const ScopedRtlShimWindow&) = delete; ScopedRtlShimWindow(ScopedRtlShimWindow&&) = delete; HWND get() const { return mWnd; } private: HWND mWnd; }; } // namespace widget } // namespace mozilla #endif // mozilla_widget_WinUtils_h__