diff options
Diffstat (limited to 'widget/windows/WinUtils.h')
-rw-r--r-- | widget/windows/WinUtils.h | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h new file mode 100644 index 0000000000..daef5ff4bc --- /dev/null +++ b/widget/windows/WinUtils.h @@ -0,0 +1,684 @@ +/* -*- 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 <windows.h> +#include <shobjidl.h> +#include <uxtheme.h> +#include <dwmapi.h> +#include <unordered_map> +#include <utility> + +// 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" + +/** + * 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<ULONG>(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<ULONG>(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 <typename F> +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>(f)) {} + + void invoke() { + WNDENUMPROC proc = &Impl::Callback; + ::EnumThreadWindows(::GetCurrentThreadId(), proc, + reinterpret_cast<LPARAM>(&f)); + } + + private: + static BOOL CALLBACK Callback(HWND hwnd, LPARAM lp) { + (*reinterpret_cast<F*>(lp))(hwnd); + return TRUE; + } + }; + + Impl(std::forward<F>(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); + + /** + * 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); + + /** + * 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<KeyPair>* 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(); + // Returns a string containing a comma-separated list of Fluent IDs + // representing the currently active pointing devices + static void GetPointerExplanation(nsAString* aExplanation); + + /** + * 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<std::pair<nsString, nsDependentString>, 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); + + static void GetClipboardFormatAsString(UINT aFormat, nsAString& aOutput); + + 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<LazyIdleThread>& aIOThread, + const bool aURLShortcut, + already_AddRefed<nsIRunnable> aRunnable); + nsresult OnFaviconDataNotAvailable(void); + + private: + ~AsyncFaviconDataReady() {} + + nsCOMPtr<nsIURI> mNewURI; + RefPtr<LazyIdleThread> mIOThread; + nsCOMPtr<nsIRunnable> 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<uint8_t[]> aData, uint32_t aStride, + uint32_t aWidth, uint32_t aHeight, + already_AddRefed<nsIRunnable> aRunnable); + + private: + virtual ~AsyncEncodeAndWriteIcon(); + + nsAutoString mIconPath; + UniquePtr<uint8_t[]> mBuffer; + nsCOMPtr<nsIRunnable> 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<nsIFile> mJumpListCacheDir; +}; + +class FaviconHelper { + public: + static const char kJumpListCacheDir[]; + static const char kShortcutCacheDir[]; + static nsresult ObtainCachedIconFile( + nsCOMPtr<nsIURI> aFaviconPageURI, nsString& aICOFilePath, + RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut, + already_AddRefed<nsIRunnable> aRunnable = nullptr); + + static nsresult GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI, + nsCOMPtr<nsIFile>& aICOFile, + bool aURLShortcut); + + static nsresult CacheIconFileFromFaviconURIAsync( + nsCOMPtr<nsIURI> aFaviconPageURI, nsCOMPtr<nsIFile> aICOFile, + RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut, + already_AddRefed<nsIRunnable> 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 ScopedRtlShimWindow { + public: + explicit ScopedRtlShimWindow(nsIWidget* aParent); + ~ScopedRtlShimWindow(); + + ScopedRtlShimWindow(const ScopedRtlShimWindow&) = delete; + ScopedRtlShimWindow(ScopedRtlShimWindow&& that) noexcept : mWnd(that.mWnd) { + that.mWnd = nullptr; + }; + + HWND get() const { return mWnd; } + + private: + HWND mWnd; +}; + +} // namespace widget +} // namespace mozilla + +#endif // mozilla_widget_WinUtils_h__ |