diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /widget/windows/WinUtils.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/windows/WinUtils.cpp')
-rw-r--r-- | widget/windows/WinUtils.cpp | 2110 |
1 files changed, 2110 insertions, 0 deletions
diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp new file mode 100644 index 000000000..149513b2f --- /dev/null +++ b/widget/windows/WinUtils.cpp @@ -0,0 +1,2110 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sts=2 sw=2 et cin: */ +/* 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/. */ + +#include "WinUtils.h" + +#include <knownfolders.h> +#include <winioctl.h> + +#include "gfxPlatform.h" +#include "gfxUtils.h" +#include "nsWindow.h" +#include "nsWindowDefs.h" +#include "KeyboardLayout.h" +#include "nsIDOMMouseEvent.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/HangMonitor.h" +#include "mozilla/Preferences.h" +#include "mozilla/RefPtr.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/Unused.h" +#include "nsIContentPolicy.h" +#include "nsContentUtils.h" + +#include "mozilla/Logging.h" + +#include "nsString.h" +#include "nsDirectoryServiceUtils.h" +#include "imgIContainer.h" +#include "imgITools.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "nsIOutputStream.h" +#include "nsNetCID.h" +#include "prtime.h" +#ifdef MOZ_PLACES +#include "mozIAsyncFavicons.h" +#endif +#include "nsIIconURI.h" +#include "nsIDownloader.h" +#include "nsINetUtil.h" +#include "nsIChannel.h" +#include "nsIObserver.h" +#include "imgIEncoder.h" +#include "nsIThread.h" +#include "MainThreadUtils.h" +#include "nsLookAndFeel.h" +#include "nsUnicharUtils.h" +#include "nsWindowsHelpers.h" + +#ifdef NS_ENABLE_TSF +#include <textstor.h> +#include "TSFTextStore.h" +#endif // #ifdef NS_ENABLE_TSF + +#include <shlwapi.h> + +mozilla::LazyLogModule gWindowsLog("Widget"); + +using namespace mozilla::gfx; + +namespace mozilla { +namespace widget { + +#define ENTRY(_msg) { #_msg, _msg } +EventMsgInfo gAllEvents[] = { + ENTRY(WM_NULL), + ENTRY(WM_CREATE), + ENTRY(WM_DESTROY), + ENTRY(WM_MOVE), + ENTRY(WM_SIZE), + ENTRY(WM_ACTIVATE), + ENTRY(WM_SETFOCUS), + ENTRY(WM_KILLFOCUS), + ENTRY(WM_ENABLE), + ENTRY(WM_SETREDRAW), + ENTRY(WM_SETTEXT), + ENTRY(WM_GETTEXT), + ENTRY(WM_GETTEXTLENGTH), + ENTRY(WM_PAINT), + ENTRY(WM_CLOSE), + ENTRY(WM_QUERYENDSESSION), + ENTRY(WM_QUIT), + ENTRY(WM_QUERYOPEN), + ENTRY(WM_ERASEBKGND), + ENTRY(WM_SYSCOLORCHANGE), + ENTRY(WM_ENDSESSION), + ENTRY(WM_SHOWWINDOW), + ENTRY(WM_SETTINGCHANGE), + ENTRY(WM_DEVMODECHANGE), + ENTRY(WM_ACTIVATEAPP), + ENTRY(WM_FONTCHANGE), + ENTRY(WM_TIMECHANGE), + ENTRY(WM_CANCELMODE), + ENTRY(WM_SETCURSOR), + ENTRY(WM_MOUSEACTIVATE), + ENTRY(WM_CHILDACTIVATE), + ENTRY(WM_QUEUESYNC), + ENTRY(WM_GETMINMAXINFO), + ENTRY(WM_PAINTICON), + ENTRY(WM_ICONERASEBKGND), + ENTRY(WM_NEXTDLGCTL), + ENTRY(WM_SPOOLERSTATUS), + ENTRY(WM_DRAWITEM), + ENTRY(WM_MEASUREITEM), + ENTRY(WM_DELETEITEM), + ENTRY(WM_VKEYTOITEM), + ENTRY(WM_CHARTOITEM), + ENTRY(WM_SETFONT), + ENTRY(WM_GETFONT), + ENTRY(WM_SETHOTKEY), + ENTRY(WM_GETHOTKEY), + ENTRY(WM_QUERYDRAGICON), + ENTRY(WM_COMPAREITEM), + ENTRY(WM_GETOBJECT), + ENTRY(WM_COMPACTING), + ENTRY(WM_COMMNOTIFY), + ENTRY(WM_WINDOWPOSCHANGING), + ENTRY(WM_WINDOWPOSCHANGED), + ENTRY(WM_POWER), + ENTRY(WM_COPYDATA), + ENTRY(WM_CANCELJOURNAL), + ENTRY(WM_NOTIFY), + ENTRY(WM_INPUTLANGCHANGEREQUEST), + ENTRY(WM_INPUTLANGCHANGE), + ENTRY(WM_TCARD), + ENTRY(WM_HELP), + ENTRY(WM_USERCHANGED), + ENTRY(WM_NOTIFYFORMAT), + ENTRY(WM_CONTEXTMENU), + ENTRY(WM_STYLECHANGING), + ENTRY(WM_STYLECHANGED), + ENTRY(WM_DISPLAYCHANGE), + ENTRY(WM_GETICON), + ENTRY(WM_SETICON), + ENTRY(WM_NCCREATE), + ENTRY(WM_NCDESTROY), + ENTRY(WM_NCCALCSIZE), + ENTRY(WM_NCHITTEST), + ENTRY(WM_NCPAINT), + ENTRY(WM_NCACTIVATE), + ENTRY(WM_GETDLGCODE), + ENTRY(WM_SYNCPAINT), + ENTRY(WM_NCMOUSEMOVE), + ENTRY(WM_NCLBUTTONDOWN), + ENTRY(WM_NCLBUTTONUP), + ENTRY(WM_NCLBUTTONDBLCLK), + ENTRY(WM_NCRBUTTONDOWN), + ENTRY(WM_NCRBUTTONUP), + ENTRY(WM_NCRBUTTONDBLCLK), + ENTRY(WM_NCMBUTTONDOWN), + ENTRY(WM_NCMBUTTONUP), + ENTRY(WM_NCMBUTTONDBLCLK), + ENTRY(EM_GETSEL), + ENTRY(EM_SETSEL), + ENTRY(EM_GETRECT), + ENTRY(EM_SETRECT), + ENTRY(EM_SETRECTNP), + ENTRY(EM_SCROLL), + ENTRY(EM_LINESCROLL), + ENTRY(EM_SCROLLCARET), + ENTRY(EM_GETMODIFY), + ENTRY(EM_SETMODIFY), + ENTRY(EM_GETLINECOUNT), + ENTRY(EM_LINEINDEX), + ENTRY(EM_SETHANDLE), + ENTRY(EM_GETHANDLE), + ENTRY(EM_GETTHUMB), + ENTRY(EM_LINELENGTH), + ENTRY(EM_REPLACESEL), + ENTRY(EM_GETLINE), + ENTRY(EM_LIMITTEXT), + ENTRY(EM_CANUNDO), + ENTRY(EM_UNDO), + ENTRY(EM_FMTLINES), + ENTRY(EM_LINEFROMCHAR), + ENTRY(EM_SETTABSTOPS), + ENTRY(EM_SETPASSWORDCHAR), + ENTRY(EM_EMPTYUNDOBUFFER), + ENTRY(EM_GETFIRSTVISIBLELINE), + ENTRY(EM_SETREADONLY), + ENTRY(EM_SETWORDBREAKPROC), + ENTRY(EM_GETWORDBREAKPROC), + ENTRY(EM_GETPASSWORDCHAR), + ENTRY(EM_SETMARGINS), + ENTRY(EM_GETMARGINS), + ENTRY(EM_GETLIMITTEXT), + ENTRY(EM_POSFROMCHAR), + ENTRY(EM_CHARFROMPOS), + ENTRY(EM_SETIMESTATUS), + ENTRY(EM_GETIMESTATUS), + ENTRY(SBM_SETPOS), + ENTRY(SBM_GETPOS), + ENTRY(SBM_SETRANGE), + ENTRY(SBM_SETRANGEREDRAW), + ENTRY(SBM_GETRANGE), + ENTRY(SBM_ENABLE_ARROWS), + ENTRY(SBM_SETSCROLLINFO), + ENTRY(SBM_GETSCROLLINFO), + ENTRY(WM_KEYDOWN), + ENTRY(WM_KEYUP), + ENTRY(WM_CHAR), + ENTRY(WM_DEADCHAR), + ENTRY(WM_SYSKEYDOWN), + ENTRY(WM_SYSKEYUP), + ENTRY(WM_SYSCHAR), + ENTRY(WM_SYSDEADCHAR), + ENTRY(WM_KEYLAST), + ENTRY(WM_IME_STARTCOMPOSITION), + ENTRY(WM_IME_ENDCOMPOSITION), + ENTRY(WM_IME_COMPOSITION), + ENTRY(WM_INITDIALOG), + ENTRY(WM_COMMAND), + ENTRY(WM_SYSCOMMAND), + ENTRY(WM_TIMER), + ENTRY(WM_HSCROLL), + ENTRY(WM_VSCROLL), + ENTRY(WM_INITMENU), + ENTRY(WM_INITMENUPOPUP), + ENTRY(WM_MENUSELECT), + ENTRY(WM_MENUCHAR), + ENTRY(WM_ENTERIDLE), + ENTRY(WM_MENURBUTTONUP), + ENTRY(WM_MENUDRAG), + ENTRY(WM_MENUGETOBJECT), + ENTRY(WM_UNINITMENUPOPUP), + ENTRY(WM_MENUCOMMAND), + ENTRY(WM_CHANGEUISTATE), + ENTRY(WM_UPDATEUISTATE), + ENTRY(WM_CTLCOLORMSGBOX), + ENTRY(WM_CTLCOLOREDIT), + ENTRY(WM_CTLCOLORLISTBOX), + ENTRY(WM_CTLCOLORBTN), + ENTRY(WM_CTLCOLORDLG), + ENTRY(WM_CTLCOLORSCROLLBAR), + ENTRY(WM_CTLCOLORSTATIC), + ENTRY(CB_GETEDITSEL), + ENTRY(CB_LIMITTEXT), + ENTRY(CB_SETEDITSEL), + ENTRY(CB_ADDSTRING), + ENTRY(CB_DELETESTRING), + ENTRY(CB_DIR), + ENTRY(CB_GETCOUNT), + ENTRY(CB_GETCURSEL), + ENTRY(CB_GETLBTEXT), + ENTRY(CB_GETLBTEXTLEN), + ENTRY(CB_INSERTSTRING), + ENTRY(CB_RESETCONTENT), + ENTRY(CB_FINDSTRING), + ENTRY(CB_SELECTSTRING), + ENTRY(CB_SETCURSEL), + ENTRY(CB_SHOWDROPDOWN), + ENTRY(CB_GETITEMDATA), + ENTRY(CB_SETITEMDATA), + ENTRY(CB_GETDROPPEDCONTROLRECT), + ENTRY(CB_SETITEMHEIGHT), + ENTRY(CB_GETITEMHEIGHT), + ENTRY(CB_SETEXTENDEDUI), + ENTRY(CB_GETEXTENDEDUI), + ENTRY(CB_GETDROPPEDSTATE), + ENTRY(CB_FINDSTRINGEXACT), + ENTRY(CB_SETLOCALE), + ENTRY(CB_GETLOCALE), + ENTRY(CB_GETTOPINDEX), + ENTRY(CB_SETTOPINDEX), + ENTRY(CB_GETHORIZONTALEXTENT), + ENTRY(CB_SETHORIZONTALEXTENT), + ENTRY(CB_GETDROPPEDWIDTH), + ENTRY(CB_SETDROPPEDWIDTH), + ENTRY(CB_INITSTORAGE), + ENTRY(CB_MSGMAX), + ENTRY(LB_ADDSTRING), + ENTRY(LB_INSERTSTRING), + ENTRY(LB_DELETESTRING), + ENTRY(LB_SELITEMRANGEEX), + ENTRY(LB_RESETCONTENT), + ENTRY(LB_SETSEL), + ENTRY(LB_SETCURSEL), + ENTRY(LB_GETSEL), + ENTRY(LB_GETCURSEL), + ENTRY(LB_GETTEXT), + ENTRY(LB_GETTEXTLEN), + ENTRY(LB_GETCOUNT), + ENTRY(LB_SELECTSTRING), + ENTRY(LB_DIR), + ENTRY(LB_GETTOPINDEX), + ENTRY(LB_FINDSTRING), + ENTRY(LB_GETSELCOUNT), + ENTRY(LB_GETSELITEMS), + ENTRY(LB_SETTABSTOPS), + ENTRY(LB_GETHORIZONTALEXTENT), + ENTRY(LB_SETHORIZONTALEXTENT), + ENTRY(LB_SETCOLUMNWIDTH), + ENTRY(LB_ADDFILE), + ENTRY(LB_SETTOPINDEX), + ENTRY(LB_GETITEMRECT), + ENTRY(LB_GETITEMDATA), + ENTRY(LB_SETITEMDATA), + ENTRY(LB_SELITEMRANGE), + ENTRY(LB_SETANCHORINDEX), + ENTRY(LB_GETANCHORINDEX), + ENTRY(LB_SETCARETINDEX), + ENTRY(LB_GETCARETINDEX), + ENTRY(LB_SETITEMHEIGHT), + ENTRY(LB_GETITEMHEIGHT), + ENTRY(LB_FINDSTRINGEXACT), + ENTRY(LB_SETLOCALE), + ENTRY(LB_GETLOCALE), + ENTRY(LB_SETCOUNT), + ENTRY(LB_INITSTORAGE), + ENTRY(LB_ITEMFROMPOINT), + ENTRY(LB_MSGMAX), + ENTRY(WM_MOUSEMOVE), + ENTRY(WM_LBUTTONDOWN), + ENTRY(WM_LBUTTONUP), + ENTRY(WM_LBUTTONDBLCLK), + ENTRY(WM_RBUTTONDOWN), + ENTRY(WM_RBUTTONUP), + ENTRY(WM_RBUTTONDBLCLK), + ENTRY(WM_MBUTTONDOWN), + ENTRY(WM_MBUTTONUP), + ENTRY(WM_MBUTTONDBLCLK), + ENTRY(WM_MOUSEWHEEL), + ENTRY(WM_MOUSEHWHEEL), + ENTRY(WM_PARENTNOTIFY), + ENTRY(WM_ENTERMENULOOP), + ENTRY(WM_EXITMENULOOP), + ENTRY(WM_NEXTMENU), + ENTRY(WM_SIZING), + ENTRY(WM_CAPTURECHANGED), + ENTRY(WM_MOVING), + ENTRY(WM_POWERBROADCAST), + ENTRY(WM_DEVICECHANGE), + ENTRY(WM_MDICREATE), + ENTRY(WM_MDIDESTROY), + ENTRY(WM_MDIACTIVATE), + ENTRY(WM_MDIRESTORE), + ENTRY(WM_MDINEXT), + ENTRY(WM_MDIMAXIMIZE), + ENTRY(WM_MDITILE), + ENTRY(WM_MDICASCADE), + ENTRY(WM_MDIICONARRANGE), + ENTRY(WM_MDIGETACTIVE), + ENTRY(WM_MDISETMENU), + ENTRY(WM_ENTERSIZEMOVE), + ENTRY(WM_EXITSIZEMOVE), + ENTRY(WM_DROPFILES), + ENTRY(WM_MDIREFRESHMENU), + ENTRY(WM_IME_SETCONTEXT), + ENTRY(WM_IME_NOTIFY), + ENTRY(WM_IME_CONTROL), + ENTRY(WM_IME_COMPOSITIONFULL), + ENTRY(WM_IME_SELECT), + ENTRY(WM_IME_CHAR), + ENTRY(WM_IME_REQUEST), + ENTRY(WM_IME_KEYDOWN), + ENTRY(WM_IME_KEYUP), + ENTRY(WM_NCMOUSEHOVER), + ENTRY(WM_MOUSEHOVER), + ENTRY(WM_MOUSELEAVE), + ENTRY(WM_CUT), + ENTRY(WM_COPY), + ENTRY(WM_PASTE), + ENTRY(WM_CLEAR), + ENTRY(WM_UNDO), + ENTRY(WM_RENDERFORMAT), + ENTRY(WM_RENDERALLFORMATS), + ENTRY(WM_DESTROYCLIPBOARD), + ENTRY(WM_DRAWCLIPBOARD), + ENTRY(WM_PAINTCLIPBOARD), + ENTRY(WM_VSCROLLCLIPBOARD), + ENTRY(WM_SIZECLIPBOARD), + ENTRY(WM_ASKCBFORMATNAME), + ENTRY(WM_CHANGECBCHAIN), + ENTRY(WM_HSCROLLCLIPBOARD), + ENTRY(WM_QUERYNEWPALETTE), + ENTRY(WM_PALETTEISCHANGING), + ENTRY(WM_PALETTECHANGED), + ENTRY(WM_HOTKEY), + ENTRY(WM_PRINT), + ENTRY(WM_PRINTCLIENT), + ENTRY(WM_THEMECHANGED), + ENTRY(WM_HANDHELDFIRST), + ENTRY(WM_HANDHELDLAST), + ENTRY(WM_AFXFIRST), + ENTRY(WM_AFXLAST), + ENTRY(WM_PENWINFIRST), + ENTRY(WM_PENWINLAST), + ENTRY(WM_APP), + ENTRY(WM_DWMCOMPOSITIONCHANGED), + ENTRY(WM_DWMNCRENDERINGCHANGED), + ENTRY(WM_DWMCOLORIZATIONCOLORCHANGED), + ENTRY(WM_DWMWINDOWMAXIMIZEDCHANGE), + ENTRY(WM_DWMSENDICONICTHUMBNAIL), + ENTRY(WM_DWMSENDICONICLIVEPREVIEWBITMAP), + ENTRY(WM_TABLET_QUERYSYSTEMGESTURESTATUS), + ENTRY(WM_GESTURE), + ENTRY(WM_GESTURENOTIFY), + ENTRY(WM_GETTITLEBARINFOEX), + {nullptr, 0x0} +}; +#undef ENTRY + +#ifdef MOZ_PLACES +NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver) +NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback) +#endif +NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable) +NS_IMPL_ISUPPORTS(AsyncDeleteIconFromDisk, nsIRunnable) +NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable) + + +const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache"; +const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache"; + +// apis available on vista and up. +WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = nullptr; +WinUtils::SHGetKnownFolderPathPtr WinUtils::sGetKnownFolderPath = nullptr; + +// We just leak these DLL HMODULEs. There's no point in calling FreeLibrary +// on them during shutdown anyway. +static const wchar_t kShellLibraryName[] = L"shell32.dll"; +static HMODULE sShellDll = nullptr; +static const wchar_t kDwmLibraryName[] = L"dwmapi.dll"; +static HMODULE sDwmDll = nullptr; + +WinUtils::DwmExtendFrameIntoClientAreaProc WinUtils::dwmExtendFrameIntoClientAreaPtr = nullptr; +WinUtils::DwmIsCompositionEnabledProc WinUtils::dwmIsCompositionEnabledPtr = nullptr; +WinUtils::DwmSetIconicThumbnailProc WinUtils::dwmSetIconicThumbnailPtr = nullptr; +WinUtils::DwmSetIconicLivePreviewBitmapProc WinUtils::dwmSetIconicLivePreviewBitmapPtr = nullptr; +WinUtils::DwmGetWindowAttributeProc WinUtils::dwmGetWindowAttributePtr = nullptr; +WinUtils::DwmSetWindowAttributeProc WinUtils::dwmSetWindowAttributePtr = nullptr; +WinUtils::DwmInvalidateIconicBitmapsProc WinUtils::dwmInvalidateIconicBitmapsPtr = nullptr; +WinUtils::DwmDefWindowProcProc WinUtils::dwmDwmDefWindowProcPtr = nullptr; +WinUtils::DwmGetCompositionTimingInfoProc WinUtils::dwmGetCompositionTimingInfoPtr = nullptr; +WinUtils::DwmFlushProc WinUtils::dwmFlushProcPtr = nullptr; + +// Prefix for path used by NT calls. +const wchar_t kNTPrefix[] = L"\\??\\"; +const size_t kNTPrefixLen = ArrayLength(kNTPrefix) - 1; + +struct CoTaskMemFreePolicy +{ + void operator()(void* aPtr) { + ::CoTaskMemFree(aPtr); + } +}; + +SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL; +EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL; +#ifdef ACCESSIBILITY +typedef NTSTATUS (NTAPI* NtTestAlertPtr)(VOID); +static NtTestAlertPtr sNtTestAlert = nullptr; +#endif + + +/* static */ +void +WinUtils::Initialize() +{ + if (!sDwmDll && IsVistaOrLater()) { + sDwmDll = ::LoadLibraryW(kDwmLibraryName); + + if (sDwmDll) { + dwmExtendFrameIntoClientAreaPtr = (DwmExtendFrameIntoClientAreaProc)::GetProcAddress(sDwmDll, "DwmExtendFrameIntoClientArea"); + dwmIsCompositionEnabledPtr = (DwmIsCompositionEnabledProc)::GetProcAddress(sDwmDll, "DwmIsCompositionEnabled"); + dwmSetIconicThumbnailPtr = (DwmSetIconicThumbnailProc)::GetProcAddress(sDwmDll, "DwmSetIconicThumbnail"); + dwmSetIconicLivePreviewBitmapPtr = (DwmSetIconicLivePreviewBitmapProc)::GetProcAddress(sDwmDll, "DwmSetIconicLivePreviewBitmap"); + dwmGetWindowAttributePtr = (DwmGetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmGetWindowAttribute"); + dwmSetWindowAttributePtr = (DwmSetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmSetWindowAttribute"); + dwmInvalidateIconicBitmapsPtr = (DwmInvalidateIconicBitmapsProc)::GetProcAddress(sDwmDll, "DwmInvalidateIconicBitmaps"); + dwmDwmDefWindowProcPtr = (DwmDefWindowProcProc)::GetProcAddress(sDwmDll, "DwmDefWindowProc"); + dwmGetCompositionTimingInfoPtr = (DwmGetCompositionTimingInfoProc)::GetProcAddress(sDwmDll, "DwmGetCompositionTimingInfo"); + dwmFlushProcPtr = (DwmFlushProc)::GetProcAddress(sDwmDll, "DwmFlush"); + } + } + + if (IsWin10OrLater()) { + HMODULE user32Dll = ::GetModuleHandleW(L"user32"); + if (user32Dll) { + sEnableNonClientDpiScaling = (EnableNonClientDpiScalingProc) + ::GetProcAddress(user32Dll, "EnableNonClientDpiScaling"); + sSetThreadDpiAwarenessContext = (SetThreadDpiAwarenessContextProc) + ::GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext"); + } + } + +#ifdef ACCESSIBILITY + sNtTestAlert = reinterpret_cast<NtTestAlertPtr>( + ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtTestAlert")); + MOZ_ASSERT(sNtTestAlert); +#endif +} + +// static +LRESULT WINAPI +WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) { + sEnableNonClientDpiScaling(hWnd); + } + return ::DefWindowProcW(hWnd, msg, wParam, lParam); +} + +// static +void +WinUtils::LogW(const wchar_t *fmt, ...) +{ + va_list args = nullptr; + if(!lstrlenW(fmt)) { + return; + } + va_start(args, fmt); + int buflen = _vscwprintf(fmt, args); + wchar_t* buffer = new wchar_t[buflen+1]; + if (!buffer) { + va_end(args); + return; + } + vswprintf(buffer, buflen, fmt, args); + va_end(args); + + // MSVC, including remote debug sessions + OutputDebugStringW(buffer); + OutputDebugStringW(L"\n"); + + int len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, nullptr, 0, nullptr, nullptr); + if (len) { + char* utf8 = new char[len]; + if (WideCharToMultiByte(CP_ACP, 0, buffer, + -1, utf8, len, nullptr, + nullptr) > 0) { + // desktop console + printf("%s\n", utf8); + NS_ASSERTION(gWindowsLog, "Called WinUtils Log() but Widget " + "log module doesn't exist!"); + MOZ_LOG(gWindowsLog, LogLevel::Error, (utf8)); + } + delete[] utf8; + } + delete[] buffer; +} + +// static +void +WinUtils::Log(const char *fmt, ...) +{ + va_list args = nullptr; + if(!strlen(fmt)) { + return; + } + va_start(args, fmt); + int buflen = _vscprintf(fmt, args); + char* buffer = new char[buflen+1]; + if (!buffer) { + va_end(args); + return; + } + vsprintf(buffer, fmt, args); + va_end(args); + + // MSVC, including remote debug sessions + OutputDebugStringA(buffer); + OutputDebugStringW(L"\n"); + + // desktop console + printf("%s\n", buffer); + + NS_ASSERTION(gWindowsLog, "Called WinUtils Log() but Widget " + "log module doesn't exist!"); + MOZ_LOG(gWindowsLog, LogLevel::Error, (buffer)); + delete[] buffer; +} + +// static +double +WinUtils::SystemScaleFactor() +{ + // The result of GetDeviceCaps won't change dynamically, as it predates + // per-monitor DPI and support for on-the-fly resolution changes. + // Therefore, we only need to look it up once. + static double systemScale = 0; + if (systemScale == 0) { + HDC screenDC = GetDC(nullptr); + systemScale = GetDeviceCaps(screenDC, LOGPIXELSY) / 96.0; + ReleaseDC(nullptr, screenDC); + + if (systemScale == 0) { + // Bug 1012487 - This can occur when the Screen DC is used off the + // main thread on windows. For now just assume a 100% DPI for this + // drawing call. + // XXX - fixme! + return 1.0; + } + } + return systemScale; +} + +#ifndef WM_DPICHANGED +typedef enum { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +typedef enum { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; +#endif + +typedef HRESULT +(WINAPI *GETDPIFORMONITORPROC)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); + +typedef HRESULT +(WINAPI *GETPROCESSDPIAWARENESSPROC)(HANDLE, PROCESS_DPI_AWARENESS*); + +GETDPIFORMONITORPROC sGetDpiForMonitor; +GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness; + +static bool +SlowIsPerMonitorDPIAware() +{ + if (IsVistaOrLater()) { + // Intentionally leak the handle. + HMODULE shcore = + LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (shcore) { + sGetDpiForMonitor = + (GETDPIFORMONITORPROC) GetProcAddress(shcore, "GetDpiForMonitor"); + sGetProcessDpiAwareness = + (GETPROCESSDPIAWARENESSPROC) GetProcAddress(shcore, "GetProcessDpiAwareness"); + } + } + PROCESS_DPI_AWARENESS dpiAwareness; + return sGetDpiForMonitor && sGetProcessDpiAwareness && + SUCCEEDED(sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness)) && + dpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE; +} + +/* static */ bool +WinUtils::IsPerMonitorDPIAware() +{ + static bool perMonitorDPIAware = SlowIsPerMonitorDPIAware(); + return perMonitorDPIAware; +} + +/* static */ +double +WinUtils::LogToPhysFactor(HMONITOR aMonitor) +{ + if (IsPerMonitorDPIAware()) { + UINT dpiX, dpiY = 96; + sGetDpiForMonitor(aMonitor ? aMonitor : GetPrimaryMonitor(), + MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + return dpiY / 96.0; + } + + return SystemScaleFactor(); +} + +/* static */ +int32_t +WinUtils::LogToPhys(HMONITOR aMonitor, double aValue) +{ + return int32_t(NS_round(aValue * LogToPhysFactor(aMonitor))); +} + +/* static */ +HMONITOR +WinUtils::GetPrimaryMonitor() +{ + const POINT pt = { 0, 0 }; + return ::MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); +} + +/* static */ +HMONITOR +WinUtils::MonitorFromRect(const gfx::Rect& rect) +{ + // convert coordinates from desktop to device pixels for MonitorFromRect + double dpiScale = + IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor()); + + RECT globalWindowBounds = { + NSToIntRound(dpiScale * rect.x), + NSToIntRound(dpiScale * rect.y), + NSToIntRound(dpiScale * (rect.x + rect.width)), + NSToIntRound(dpiScale * (rect.y + rect.height)) + }; + + return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST); +} + +#ifdef ACCESSIBILITY +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +static Atomic<bool> sAPCPending; + +/* static */ +void +WinUtils::SetAPCPending() +{ + sAPCPending = true; +} +#endif // ACCESSIBILITY + +/* static */ +bool +WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage, UINT aOption) +{ +#ifdef ACCESSIBILITY + if (NS_IsMainThread() && sAPCPending.exchange(false)) { + while (sNtTestAlert() != STATUS_SUCCESS) ; + } +#endif +#ifdef NS_ENABLE_TSF + RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump(); + if (msgPump) { + BOOL ret = FALSE; + HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, + aOption, &ret); + NS_ENSURE_TRUE(SUCCEEDED(hr), false); + return ret; + } +#endif // #ifdef NS_ENABLE_TSF + return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption); +} + +/* static */ +bool +WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, + UINT aLastMessage) +{ +#ifdef NS_ENABLE_TSF + RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump(); + if (msgPump) { + BOOL ret = FALSE; + HRESULT hr = msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, + &ret); + NS_ENSURE_TRUE(SUCCEEDED(hr), false); + return ret; + } +#endif // #ifdef NS_ENABLE_TSF + return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage); +} + +#if defined(ACCESSIBILITY) +static DWORD +GetWaitFlags() +{ + DWORD result = MWMO_INPUTAVAILABLE; + if (IsVistaOrLater() && XRE_IsContentProcess()) { + result |= MWMO_ALERTABLE; + } + return result; +} +#endif + +/* static */ +void +WinUtils::WaitForMessage(DWORD aTimeoutMs) +{ +#if defined(ACCESSIBILITY) + static const DWORD waitFlags = GetWaitFlags(); +#else + const DWORD waitFlags = MWMO_INPUTAVAILABLE; +#endif + + const DWORD waitStart = ::GetTickCount(); + DWORD elapsed = 0; + while (true) { + if (aTimeoutMs != INFINITE) { + elapsed = ::GetTickCount() - waitStart; + } + if (elapsed >= aTimeoutMs) { + break; + } + DWORD result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed, + MOZ_QS_ALLEVENT, waitFlags); + NS_WARNING_ASSERTION(result != WAIT_FAILED, "Wait failed"); + if (result == WAIT_TIMEOUT) { + break; + } +#if defined(ACCESSIBILITY) + if (result == WAIT_IO_COMPLETION) { + if (NS_IsMainThread()) { + if (sAPCPending.exchange(false)) { + // Clear out any pending APCs + while (sNtTestAlert() != STATUS_SUCCESS) ; + } + // We executed an APC that would have woken up the hang monitor. Since + // there are no more APCs pending and we are now going to sleep again, + // we should notify the hang monitor. + mozilla::HangMonitor::Suspend(); + } + continue; + } +#endif // defined(ACCESSIBILITY) + + // Sent messages (via SendMessage and friends) are processed differently + // than queued messages (via PostMessage); the destination window procedure + // of the sent message is called during (Get|Peek)Message. Since PeekMessage + // does not tell us whether it processed any sent messages, we need to query + // this ahead of time. + bool haveSentMessagesPending = + (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0; + + MSG msg = {0}; + if (haveSentMessagesPending || + ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) { + break; + } + // The message is intended for another thread that has been synchronized + // with our input queue; yield to give other threads an opportunity to + // process the message. This should prevent busy waiting if resumed due + // to another thread's message. + ::SwitchToThread(); + } +} + +/* static */ +bool +WinUtils::GetRegistryKey(HKEY aRoot, + char16ptr_t aKeyName, + char16ptr_t aValueName, + wchar_t* aBuffer, + DWORD aBufferLength) +{ + NS_PRECONDITION(aKeyName, "The key name is NULL"); + + HKEY key; + LONG result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key); + if (result != ERROR_SUCCESS) { + result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key); + if (result != ERROR_SUCCESS) { + return false; + } + } + + DWORD type; + result = + ::RegQueryValueExW(key, aValueName, nullptr, &type, (BYTE*) aBuffer, + &aBufferLength); + ::RegCloseKey(key); + if (result != ERROR_SUCCESS || + (type != REG_SZ && type != REG_EXPAND_SZ)) { + return false; + } + if (aBuffer) { + aBuffer[aBufferLength / sizeof(*aBuffer) - 1] = 0; + } + return true; +} + +/* static */ +bool +WinUtils::HasRegistryKey(HKEY aRoot, char16ptr_t aKeyName) +{ + MOZ_ASSERT(aRoot, "aRoot must not be NULL"); + MOZ_ASSERT(aKeyName, "aKeyName must not be NULL"); + HKEY key; + LONG result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key); + if (result != ERROR_SUCCESS) { + result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key); + if (result != ERROR_SUCCESS) { + return false; + } + } + ::RegCloseKey(key); + return true; +} + +/* static */ +HWND +WinUtils::GetTopLevelHWND(HWND aWnd, + bool aStopIfNotChild, + bool aStopIfNotPopup) +{ + HWND curWnd = aWnd; + HWND topWnd = nullptr; + + while (curWnd) { + topWnd = curWnd; + + if (aStopIfNotChild) { + DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE); + + VERIFY_WINDOW_STYLE(style); + + if (!(style & WS_CHILD)) // first top-level window + break; + } + + HWND upWnd = ::GetParent(curWnd); // Parent or owner (if has no parent) + + // GetParent will only return the owner if the passed in window + // has the WS_POPUP style. + if (!upWnd && !aStopIfNotPopup) { + upWnd = ::GetWindow(curWnd, GW_OWNER); + } + curWnd = upWnd; + } + + return topWnd; +} + +static const wchar_t* +GetNSWindowPropName() +{ + static wchar_t sPropName[40] = L""; + if (!*sPropName) { + _snwprintf(sPropName, 39, L"MozillansIWidgetPtr%u", + ::GetCurrentProcessId()); + sPropName[39] = '\0'; + } + return sPropName; +} + +/* static */ +bool +WinUtils::SetNSWindowBasePtr(HWND aWnd, nsWindowBase* aWidget) +{ + if (!aWidget) { + ::RemovePropW(aWnd, GetNSWindowPropName()); + return true; + } + return ::SetPropW(aWnd, GetNSWindowPropName(), (HANDLE)aWidget); +} + +/* static */ +nsWindowBase* +WinUtils::GetNSWindowBasePtr(HWND aWnd) +{ + return static_cast<nsWindowBase*>(::GetPropW(aWnd, GetNSWindowPropName())); +} + +/* static */ +nsWindow* +WinUtils::GetNSWindowPtr(HWND aWnd) +{ + return static_cast<nsWindow*>(::GetPropW(aWnd, GetNSWindowPropName())); +} + +static BOOL CALLBACK +AddMonitor(HMONITOR, HDC, LPRECT, LPARAM aParam) +{ + (*(int32_t*)aParam)++; + return TRUE; +} + +/* static */ +int32_t +WinUtils::GetMonitorCount() +{ + int32_t monitorCount = 0; + EnumDisplayMonitors(nullptr, nullptr, AddMonitor, (LPARAM)&monitorCount); + return monitorCount; +} + +/* static */ +bool +WinUtils::IsOurProcessWindow(HWND aWnd) +{ + if (!aWnd) { + return false; + } + DWORD processId = 0; + ::GetWindowThreadProcessId(aWnd, &processId); + return (processId == ::GetCurrentProcessId()); +} + +/* static */ +HWND +WinUtils::FindOurProcessWindow(HWND aWnd) +{ + for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) { + if (IsOurProcessWindow(wnd)) { + return wnd; + } + } + return nullptr; +} + +static bool +IsPointInWindow(HWND aWnd, const POINT& aPointInScreen) +{ + RECT bounds; + if (!::GetWindowRect(aWnd, &bounds)) { + return false; + } + + return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right && + aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom); +} + +/** + * FindTopmostWindowAtPoint() returns the topmost child window (topmost means + * forground in this context) of aWnd. + */ + +static HWND +FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen) +{ + if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) { + return nullptr; + } + + HWND childWnd = ::GetTopWindow(aWnd); + while (childWnd) { + HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen); + if (topmostWnd) { + return topmostWnd; + } + childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT); + } + + return aWnd; +} + +struct FindOurWindowAtPointInfo +{ + POINT mInPointInScreen; + HWND mOutWnd; +}; + +static BOOL CALLBACK +FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM) +{ + if (!WinUtils::IsOurProcessWindow(aWnd)) { + // This isn't one of our top-level windows; continue enumerating. + return TRUE; + } + + // Get the top-most child window under the point. If there's no child + // window, and the point is within the top-level window, then the top-level + // window will be returned. (This is the usual case. A child window + // would be returned for plugins.) + FindOurWindowAtPointInfo* info = + reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM); + HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen); + if (!childWnd) { + // This window doesn't contain the point; continue enumerating. + return TRUE; + } + + // Return the HWND and stop enumerating. + info->mOutWnd = childWnd; + return FALSE; +} + +/* static */ +HWND +WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen) +{ + FindOurWindowAtPointInfo info; + info.mInPointInScreen = aPointInScreen; + info.mOutWnd = nullptr; + + // This will enumerate all top-level windows in order from top to bottom. + EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info)); + return info.mOutWnd; +} + +/* static */ +UINT +WinUtils::GetInternalMessage(UINT aNativeMessage) +{ + switch (aNativeMessage) { + case WM_MOUSEWHEEL: + return MOZ_WM_MOUSEVWHEEL; + case WM_MOUSEHWHEEL: + return MOZ_WM_MOUSEHWHEEL; + case WM_VSCROLL: + return MOZ_WM_VSCROLL; + case WM_HSCROLL: + return MOZ_WM_HSCROLL; + default: + return aNativeMessage; + } +} + +/* static */ +UINT +WinUtils::GetNativeMessage(UINT aInternalMessage) +{ + switch (aInternalMessage) { + case MOZ_WM_MOUSEVWHEEL: + return WM_MOUSEWHEEL; + case MOZ_WM_MOUSEHWHEEL: + return WM_MOUSEHWHEEL; + case MOZ_WM_VSCROLL: + return WM_VSCROLL; + case MOZ_WM_HSCROLL: + return WM_HSCROLL; + default: + return aInternalMessage; + } +} + +/* static */ +uint16_t +WinUtils::GetMouseInputSource() +{ + int32_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; + LPARAM lParamExtraInfo = ::GetMessageExtraInfo(); + if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) { + inputSource = (lParamExtraInfo & TABLET_INK_TOUCH) ? + nsIDOMMouseEvent::MOZ_SOURCE_TOUCH : nsIDOMMouseEvent::MOZ_SOURCE_PEN; + } + return static_cast<uint16_t>(inputSource); +} + +/* static */ +uint16_t +WinUtils::GetMousePointerID() +{ + LPARAM lParamExtraInfo = ::GetMessageExtraInfo(); + return lParamExtraInfo & TABLET_INK_ID_MASK; +} + +/* static */ +bool +WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage) +{ + const uint32_t MOZ_T_I_SIGNATURE = TABLET_INK_TOUCH | TABLET_INK_SIGNATURE; + const uint32_t MOZ_T_I_CHECK_TCH = TABLET_INK_TOUCH | TABLET_INK_CHECK; + return ((aEventMessage == eMouseMove || aEventMessage == eMouseDown || + aEventMessage == eMouseUp || aEventMessage == eMouseDoubleClick) && + (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE) == MOZ_T_I_CHECK_TCH); +} + +/* static */ +MSG +WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) +{ + MSG msg; + msg.message = aMessage; + msg.wParam = wParam; + msg.lParam = lParam; + msg.hwnd = aWnd; + return msg; +} + +/* static */ +HRESULT +WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, + REFIID riid, void **ppv) +{ + if (sCreateItemFromParsingName) { + return sCreateItemFromParsingName(pszPath, pbc, riid, ppv); + } + + if (!sShellDll) { + sShellDll = ::LoadLibraryW(kShellLibraryName); + if (!sShellDll) { + return false; + } + } + + sCreateItemFromParsingName = (SHCreateItemFromParsingNamePtr) + GetProcAddress(sShellDll, "SHCreateItemFromParsingName"); + if (!sCreateItemFromParsingName) + return E_FAIL; + + return sCreateItemFromParsingName(pszPath, pbc, riid, ppv); +} + +/* static */ +HRESULT +WinUtils::SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath) +{ + if (sGetKnownFolderPath) { + return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath); + } + + if (!sShellDll) { + sShellDll = ::LoadLibraryW(kShellLibraryName); + if (!sShellDll) { + return false; + } + } + + sGetKnownFolderPath = (SHGetKnownFolderPathPtr) + GetProcAddress(sShellDll, "SHGetKnownFolderPath"); + if (!sGetKnownFolderPath) + return E_FAIL; + + return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath); +} + +static BOOL +WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam) +{ + *((HWND*)lParam) = hwnd; + return FALSE; +} + +/* static */ +void +WinUtils::InvalidatePluginAsWorkaround(nsIWidget* aWidget, + const LayoutDeviceIntRect& aRect) +{ + aWidget->Invalidate(aRect); + + // XXX - Even more evil workaround!! See bug 762948, flash's bottom + // level sandboxed window doesn't seem to get our invalidate. We send + // an invalidate to it manually. This is totally specialized for this + // bug, for other child window structures this will just be a more or + // less bogus invalidate but since that should not have any bad + // side-effects this will have to do for now. + HWND current = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); + + RECT windowRect; + RECT parentRect; + + ::GetWindowRect(current, &parentRect); + + HWND next = current; + do { + current = next; + ::EnumChildWindows(current, &EnumFirstChild, (LPARAM)&next); + ::GetWindowRect(next, &windowRect); + // This is relative to the screen, adjust it to be relative to the + // window we're reconfiguring. + windowRect.left -= parentRect.left; + windowRect.top -= parentRect.top; + } while (next != current && windowRect.top == 0 && windowRect.left == 0); + + if (windowRect.top == 0 && windowRect.left == 0) { + RECT rect; + rect.left = aRect.x; + rect.top = aRect.y; + rect.right = aRect.XMost(); + rect.bottom = aRect.YMost(); + + ::InvalidateRect(next, &rect, FALSE); + } +} + +#ifdef MOZ_PLACES +/************************************************************************ + * Constructs as AsyncFaviconDataReady Object + * @param aIOThread : the thread which performs the action + * @param aURLShortcut : Differentiates between (false)Jumplistcache and (true)Shortcutcache + ************************************************************************/ + +AsyncFaviconDataReady::AsyncFaviconDataReady(nsIURI *aNewURI, + nsCOMPtr<nsIThread> &aIOThread, + const bool aURLShortcut): + mNewURI(aNewURI), + mIOThread(aIOThread), + mURLShortcut(aURLShortcut) +{ +} + +NS_IMETHODIMP +myDownloadObserver::OnDownloadComplete(nsIDownloader *downloader, + nsIRequest *request, + nsISupports *ctxt, + nsresult status, + nsIFile *result) +{ + return NS_OK; +} + +nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) +{ + if (!mURLShortcut) { + return NS_OK; + } + + nsCOMPtr<nsIFile> icoFile; + nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> mozIconURI; + rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32"); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewChannel(getter_AddRefs(channel), + mozIconURI, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_INTERNAL_IMAGE); + + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver; + nsCOMPtr<nsIStreamListener> listener; + rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile); + NS_ENSURE_SUCCESS(rv, rv); + + return channel->AsyncOpen2(listener); +} + +NS_IMETHODIMP +AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI, + uint32_t aDataLen, + const uint8_t *aData, + const nsACString &aMimeType) +{ + if (!aDataLen || !aData) { + if (mURLShortcut) { + OnFaviconDataNotAvailable(); + } + + return NS_OK; + } + + nsCOMPtr<nsIFile> icoFile; + nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString path; + rv = icoFile->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + // Convert the obtained favicon data to an input stream + nsCOMPtr<nsIInputStream> stream; + rv = NS_NewByteInputStream(getter_AddRefs(stream), + reinterpret_cast<const char*>(aData), + aDataLen, + NS_ASSIGNMENT_DEPEND); + NS_ENSURE_SUCCESS(rv, rv); + + // Decode the image from the format it was returned to us in (probably PNG) + nsCOMPtr<imgIContainer> container; + nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); + rv = imgtool->DecodeImageData(stream, aMimeType, + getter_AddRefs(container)); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<SourceSurface> surface = + container->GetFrame(imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE | + imgIContainer::FLAG_ASYNC_NOTIFY); + NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); + + RefPtr<DataSourceSurface> dataSurface; + IntSize size; + + if (mURLShortcut) { + // Create a 48x48 surface and paint the icon into the central 16x16 rect. + size.width = 48; + size.height = 48; + dataSurface = + Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); + + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { + return NS_ERROR_FAILURE; + } + + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + map.mData, + dataSurface->GetSize(), + map.mStride, + dataSurface->GetFormat()); + if (!dt) { + gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in CreateDrawTargetForData"; + return NS_ERROR_OUT_OF_MEMORY; + } + dt->FillRect(Rect(0, 0, size.width, size.height), + ColorPattern(Color(1.0f, 1.0f, 1.0f, 1.0f))); + dt->DrawSurface(surface, + Rect(16, 16, 16, 16), + Rect(Point(0, 0), + Size(surface->GetSize().width, surface->GetSize().height))); + + dataSurface->Unmap(); + } else { + // By using the input image surface's size, we may end up encoding + // to a different size than a 16x16 (or bigger for higher DPI) ICO, but + // Windows will resize appropriately for us. If we want to encode ourselves + // one day because we like our resizing better, we'd have to manually + // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and + // SM_CYSMICON. We don't support resizing images asynchronously at the + // moment anyway so getting the DPI aware icon size won't help. + size.width = surface->GetSize().width; + size.height = surface->GetSize().height; + dataSurface = surface->GetDataSurface(); + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); + } + + // Allocate a new buffer that we own and can use out of line in + // another thread. + UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface); + if (!data) { + return NS_ERROR_OUT_OF_MEMORY; + } + int32_t stride = 4 * size.width; + + // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer + nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(path, Move(data), + stride, + size.width, + size.height, + mURLShortcut); + mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); + + return NS_OK; +} +#endif + +// Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed in +AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(const nsAString &aIconPath, + UniquePtr<uint8_t[]> aBuffer, + uint32_t aStride, + uint32_t aWidth, + uint32_t aHeight, + const bool aURLShortcut) : + mURLShortcut(aURLShortcut), + mIconPath(aIconPath), + mBuffer(Move(aBuffer)), + mStride(aStride), + mWidth(aWidth), + mHeight(aHeight) +{ +} + +NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() +{ + NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread."); + + // Note that since we're off the main thread we can't use + // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget() + RefPtr<DataSourceSurface> surface = + Factory::CreateWrappingDataSourceSurface(mBuffer.get(), mStride, + IntSize(mWidth, mHeight), + SurfaceFormat::B8G8R8A8); + + FILE* file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb"); + if (!file) { + // Maybe the directory doesn't exist; try creating it, then fopen again. + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1"); + if (comFile) { + //NS_ConvertUTF8toUTF16 utf16path(mIconPath); + rv = comFile->InitWithPath(mIconPath); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIFile> dirPath; + comFile->GetParent(getter_AddRefs(dirPath)); + if (dirPath) { + rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) { + file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb"); + if (!file) { + rv = NS_ERROR_FAILURE; + } + } + } + } + } + if (!file) { + return rv; + } + } + nsresult rv = + gfxUtils::EncodeSourceSurface(surface, + NS_LITERAL_CSTRING("image/vnd.microsoft.icon"), + EmptyString(), + gfxUtils::eBinaryEncode, + file); + fclose(file); + NS_ENSURE_SUCCESS(rv, rv); + + if (mURLShortcut) { + SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0); + } + return rv; +} + +AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() +{ +} + +AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath) + : mIconPath(aIconPath) +{ +} + +NS_IMETHODIMP AsyncDeleteIconFromDisk::Run() +{ + // Construct the parent path of the passed in path + nsCOMPtr<nsIFile> icoFile = do_CreateInstance("@mozilla.org/file/local;1"); + NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE); + nsresult rv = icoFile->InitWithPath(mIconPath); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if the cached ICO file exists + bool exists; + rv = icoFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + // Check that we aren't deleting some arbitrary file that is not an icon + if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) { + // Check if the cached ICO file exists + bool exists; + if (NS_FAILED(icoFile->Exists(&exists)) || !exists) + return NS_ERROR_FAILURE; + + // We found an ICO file that exists, so we should remove it + icoFile->Remove(false); + } + + return NS_OK; +} + +AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk() +{ +} + +AsyncDeleteAllFaviconsFromDisk:: + AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent) + : mIgnoreRecent(aIgnoreRecent) +{ + // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main + // threads, as it reads a pref, so cache its value here. + mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600; +} + +NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() +{ + // Construct the path of our jump list cache + nsCOMPtr<nsIFile> jumpListCacheDir; + nsresult rv = NS_GetSpecialDirectory("ProfLDS", + getter_AddRefs(jumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = jumpListCacheDir->AppendNative( + nsDependentCString(FaviconHelper::kJumpListCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISimpleEnumerator> entries; + rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + // Loop through each directory entry and remove all ICO files found + do { + bool hasMore = false; + if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore) + break; + + nsCOMPtr<nsISupports> supp; + if (NS_FAILED(entries->GetNext(getter_AddRefs(supp)))) + break; + + nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp)); + nsAutoString path; + if (NS_FAILED(currFile->GetPath(path))) + continue; + + if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) { + // Check if the cached ICO file exists + bool exists; + if (NS_FAILED(currFile->Exists(&exists)) || !exists) + continue; + + if (mIgnoreRecent) { + // Check to make sure the icon wasn't just recently created. + // If it was created recently, don't delete it yet. + int64_t fileModTime = 0; + rv = currFile->GetLastModifiedTime(&fileModTime); + fileModTime /= PR_MSEC_PER_SEC; + // If the icon is older than the regeneration time (+ 10 min to be + // safe), then it's old and we can get rid of it. + // This code is only hit directly after a regeneration. + int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC); + if (NS_FAILED(rv) || + (nowTime - fileModTime) < mIcoNoDeleteSeconds) { + continue; + } + } + + // We found an ICO file that exists, so we should remove it + currFile->Remove(false); + } + } while(true); + + return NS_OK; +} + +AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() +{ +} + + +/* + * (static) If the data is available, will return the path on disk where + * the favicon for page aFaviconPageURI is stored. If the favicon does not + * exist, or its cache is expired, this method will kick off an async request + * for the icon so that next time the method is called it will be available. + * @param aFaviconPageURI The URI of the page to obtain + * @param aICOFilePath The path of the icon file + * @param aIOThread The thread to perform the Fetch on + * @param aURLShortcut to distinguish between jumplistcache(false) and shortcutcache(true) + */ +nsresult FaviconHelper::ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI, + nsString &aICOFilePath, + nsCOMPtr<nsIThread> &aIOThread, + bool aURLShortcut) +{ + // Obtain the ICO file path + nsCOMPtr<nsIFile> icoFile; + nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if the cached ICO file already exists + bool exists; + rv = icoFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + + // Obtain the file's last modification date in seconds + int64_t fileModTime = 0; + rv = icoFile->GetLastModifiedTime(&fileModTime); + fileModTime /= PR_MSEC_PER_SEC; + int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout(); + int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC); + + // If the last mod call failed or the icon is old then re-cache it + // This check is in case the favicon of a page changes + // the next time we try to build the jump list, the data will be available. + if (NS_FAILED(rv) || + (nowTime - fileModTime) > icoReCacheSecondsTimeout) { + CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut); + return NS_ERROR_NOT_AVAILABLE; + } + } else { + + // The file does not exist yet, obtain it async from the favicon service so that + // the next time we try to build the jump list it'll be available. + CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut); + return NS_ERROR_NOT_AVAILABLE; + } + + // The icoFile is filled with a path that exists, get its path + rv = icoFile->GetPath(aICOFilePath); + return rv; +} + +nsresult FaviconHelper::HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash, + nsIURI *aUri, + nsACString& aUriHash) +{ + if (!aUri) + return NS_ERROR_INVALID_ARG; + + nsAutoCString spec; + nsresult rv = aUri->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aCryptoHash) { + aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aCryptoHash->Init(nsICryptoHash::MD5); + NS_ENSURE_SUCCESS(rv, rv); + rv = aCryptoHash->Update(reinterpret_cast<const uint8_t*>(spec.BeginReading()), + spec.Length()); + NS_ENSURE_SUCCESS(rv, rv); + rv = aCryptoHash->Finish(true, aUriHash); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + + + +// (static) Obtains the ICO file for the favicon at page aFaviconPageURI +// If successful, the file path on disk is in the format: +// <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico +nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI, + nsCOMPtr<nsIFile> &aICOFile, + bool aURLShortcut) +{ + // Hash the input URI and replace any / with _ + nsAutoCString inputURIHash; + nsCOMPtr<nsICryptoHash> cryptoHash; + nsresult rv = HashURI(cryptoHash, aFaviconPageURI, + inputURIHash); + NS_ENSURE_SUCCESS(rv, rv); + char* cur = inputURIHash.BeginWriting(); + char* end = inputURIHash.EndWriting(); + for (; cur < end; ++cur) { + if ('/' == *cur) { + *cur = '_'; + } + } + + // Obtain the local profile directory and construct the output icon file path + rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile)); + NS_ENSURE_SUCCESS(rv, rv); + if (!aURLShortcut) + rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir)); + else + rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + + // Append the icon extension + inputURIHash.AppendLiteral(".ico"); + rv = aICOFile->AppendNative(inputURIHash); + + return rv; +} + +// (static) Asynchronously creates a cached ICO file on disk for the favicon of +// page aFaviconPageURI and stores it to disk at the path of aICOFile. +nsresult + FaviconHelper::CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI, + nsCOMPtr<nsIFile> aICOFile, + nsCOMPtr<nsIThread> &aIOThread, + bool aURLShortcut) +{ +#ifdef MOZ_PLACES + // Obtain the favicon service and get the favicon for the specified page + nsCOMPtr<mozIAsyncFavicons> favIconSvc( + do_GetService("@mozilla.org/browser/favicon-service;1")); + NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE); + + nsCOMPtr<nsIFaviconDataCallback> callback = + new mozilla::widget::AsyncFaviconDataReady(aFaviconPageURI, + aIOThread, + aURLShortcut); + + favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback); +#endif + return NS_OK; +} + +// Obtains the jump list 'ICO cache timeout in seconds' pref +int32_t FaviconHelper::GetICOCacheSecondsTimeout() { + + // Only obtain the setting at most once from the pref service. + // In the rare case that 2 threads call this at the same + // time it is no harm and we will simply obtain the pref twice. + // None of the taskbar list prefs are currently updated via a + // pref observer so I think this should suffice. + const int32_t kSecondsPerDay = 86400; + static bool alreadyObtained = false; + static int32_t icoReCacheSecondsTimeout = kSecondsPerDay; + if (alreadyObtained) { + return icoReCacheSecondsTimeout; + } + + // Obtain the pref + const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds"; + icoReCacheSecondsTimeout = Preferences::GetInt(PREF_ICOTIMEOUT, + kSecondsPerDay); + alreadyObtained = true; + return icoReCacheSecondsTimeout; +} + + + + +/* static */ +bool +WinUtils::GetShellItemPath(IShellItem* aItem, + nsString& aResultString) +{ + NS_ENSURE_TRUE(aItem, false); + LPWSTR str = nullptr; + if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) + return false; + aResultString.Assign(str); + CoTaskMemFree(str); + return !aResultString.IsEmpty(); +} + +/* static */ +nsIntRegion +WinUtils::ConvertHRGNToRegion(HRGN aRgn) +{ + NS_ASSERTION(aRgn, "Don't pass NULL region here"); + + nsIntRegion rgn; + + DWORD size = ::GetRegionData(aRgn, 0, nullptr); + AutoTArray<uint8_t,100> buffer; + buffer.SetLength(size); + + RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements()); + if (!::GetRegionData(aRgn, size, data)) + return rgn; + + if (data->rdh.nCount > MAX_RECTS_IN_REGION) { + rgn = ToIntRect(data->rdh.rcBound); + return rgn; + } + + RECT* rects = reinterpret_cast<RECT*>(data->Buffer); + for (uint32_t i = 0; i < data->rdh.nCount; ++i) { + RECT* r = rects + i; + rgn.Or(rgn, ToIntRect(*r)); + } + + return rgn; +} + +nsIntRect +WinUtils::ToIntRect(const RECT& aRect) +{ + return nsIntRect(aRect.left, aRect.top, + aRect.right - aRect.left, + aRect.bottom - aRect.top); +} + +/* static */ +bool +WinUtils::IsIMEEnabled(const InputContext& aInputContext) +{ + return IsIMEEnabled(aInputContext.mIMEState.mEnabled); +} + +/* static */ +bool +WinUtils::IsIMEEnabled(IMEState::Enabled aIMEState) +{ + return (aIMEState == IMEState::ENABLED || + aIMEState == IMEState::PLUGIN); +} + +/* static */ +void +WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray, + uint32_t aModifiers) +{ + for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) { + const uint32_t* map = sModifierKeyMap[i]; + if (aModifiers & map[0]) { + aArray->AppendElement(KeyPair(map[1], map[2])); + } + } +} + +/* static */ +bool +WinUtils::ShouldHideScrollbars() +{ + return false; +} + +// This is in use here and in dom/events/TouchEvent.cpp +/* static */ +uint32_t +WinUtils::IsTouchDeviceSupportPresent() +{ + int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER); + return (touchCapabilities & NID_READY) && + (touchCapabilities & (NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH)); +} + +/* static */ +uint32_t +WinUtils::GetMaxTouchPoints() +{ + if (IsWin7OrLater() && IsTouchDeviceSupportPresent()) { + return GetSystemMetrics(SM_MAXIMUMTOUCHES); + } + return 0; +} + +typedef DWORD (WINAPI * GetFinalPathNameByHandlePtr)(HANDLE hFile, + LPTSTR lpszFilePath, + DWORD cchFilePath, + DWORD dwFlags); + +/* static */ +bool +WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath) +{ + // Users folder was introduced with Vista. + if (!IsVistaOrLater()) { + return true; + } + + wchar_t path[MAX_PATH] = { 0 }; + + nsAutoHandle handle( + ::CreateFileW(aPath.c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr)); + + if (handle == INVALID_HANDLE_VALUE) { + return false; + } + + // GetFinalPathNameByHandleW is a Vista and later API. Since ESR builds with + // XP support still, we need to load the function manually. + GetFinalPathNameByHandlePtr getFinalPathNameFnPtr = nullptr; + HMODULE kernel32Dll = ::GetModuleHandleW(L"Kernel32"); + if (kernel32Dll) { + getFinalPathNameFnPtr = (GetFinalPathNameByHandlePtr) + ::GetProcAddress(kernel32Dll, "GetFinalPathNameByHandleW"); + } + + if (!getFinalPathNameFnPtr) { + return false; + } + + DWORD pathLen = getFinalPathNameFnPtr( + handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (pathLen == 0 || pathLen >= MAX_PATH) { + return false; + } + aPath = path; + + // GetFinalPathNameByHandle sticks a '\\?\' in front of the path, + // but that confuses some APIs so strip it off. It will also put + // '\\?\UNC\' in front of network paths, we convert that to '\\'. + if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) { + aPath.erase(2, 6); + } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) { + aPath.erase(0, 4); + } + + return true; +} + +/* static */ +bool +WinUtils::SanitizePath(const wchar_t* aInputPath, nsAString& aOutput) +{ + aOutput.Truncate(); + wchar_t buffer[MAX_PATH + 1] = {0}; + if (!PathCanonicalizeW(buffer, aInputPath)) { + return false; + } + wchar_t longBuffer[MAX_PATH + 1] = {0}; + DWORD longResult = GetLongPathNameW(buffer, longBuffer, MAX_PATH); + if (longResult == 0 || longResult > MAX_PATH - 1) { + return false; + } + aOutput.SetLength(MAX_PATH + 1); + wchar_t* output = reinterpret_cast<wchar_t*>(aOutput.BeginWriting()); + if (!PathUnExpandEnvStringsW(longBuffer, output, MAX_PATH)) { + return false; + } + // Truncate to correct length + aOutput.Truncate(wcslen(char16ptr_t(aOutput.BeginReading()))); + MOZ_ASSERT(aOutput.Length() <= MAX_PATH); + return true; +} + +/** + * This function provides an array of (system path, substitution) pairs that are + * considered to be acceptable with respect to privacy, for the purposes of + * submitting within telemetry or crash reports. + * + * The substitution string's void flag may be set. If it is, no subsitution is + * necessary. Otherwise, the consumer should replace the system path with the + * substitution. + * + * @see GetAppInitDLLs for an example of its usage. + */ +/* static */ +void +WinUtils::GetWhitelistedPaths( + nsTArray<mozilla::Pair<nsString,nsDependentString>>& aOutput) +{ + aOutput.Clear(); + aOutput.AppendElement(mozilla::MakePair( + nsString(NS_LITERAL_STRING("%ProgramFiles%")), + nsDependentString())); + // When no substitution is required, set the void flag + aOutput.LastElement().second().SetIsVoid(true); + wchar_t tmpPath[MAX_PATH + 1] = {0}; + if (GetTempPath(MAX_PATH, tmpPath)) { + // GetTempPath's result always ends with a backslash, which we don't want + uint32_t tmpPathLen = wcslen(tmpPath); + if (tmpPathLen) { + tmpPath[tmpPathLen - 1] = 0; + } + nsAutoString cleanTmpPath; + if (SanitizePath(tmpPath, cleanTmpPath)) { + aOutput.AppendElement(mozilla::MakePair(nsString(cleanTmpPath), + nsDependentString(L"%TEMP%"))); + } + } +} + +/** + * This function is located here (as opposed to nsSystemInfo or elsewhere) + * because we need to gather this information as early as possible during + * startup. + */ +/* static */ +bool +WinUtils::GetAppInitDLLs(nsAString& aOutput) +{ + aOutput.Truncate(); + HKEY hkey = NULL; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", + 0, KEY_QUERY_VALUE, &hkey)) { + return false; + } + nsAutoRegKey key(hkey); + LONG status; + if (IsVistaOrLater()) { + const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs"; + DWORD loadAppInitDLLs = 0; + DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs); + status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr, + nullptr, (LPBYTE)&loadAppInitDLLs, + &loadAppInitDLLsLen); + if (status != ERROR_SUCCESS) { + return false; + } + if (!loadAppInitDLLs) { + // If loadAppInitDLLs is zero then AppInit_DLLs is disabled. + // In this case we'll return true along with an empty output string. + return true; + } + } + DWORD numBytes = 0; + const wchar_t kAppInitDLLs[] = L"AppInit_DLLs"; + // Query for required buffer size + status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr, + &numBytes); + if (status != ERROR_SUCCESS) { + return false; + } + // Allocate the buffer and query for the actual data + mozilla::UniquePtr<wchar_t[]> data = + mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t)); + status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, + nullptr, (LPBYTE)data.get(), &numBytes); + if (status != ERROR_SUCCESS) { + return false; + } + nsTArray<mozilla::Pair<nsString,nsDependentString>> whitelistedPaths; + GetWhitelistedPaths(whitelistedPaths); + // For each token, split up the filename components and then check the + // name of the file. + const wchar_t kDelimiters[] = L", "; + wchar_t* tokenContext = nullptr; + wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext); + while (token) { + nsAutoString cleanPath; + // Since these paths are short paths originating from the registry, we need + // to canonicalize them, lengthen them, and sanitize them before we can + // check them against the whitelist + if (SanitizePath(token, cleanPath)) { + bool needsStrip = true; + for (uint32_t i = 0; i < whitelistedPaths.Length(); ++i) { + const nsString& testPath = whitelistedPaths[i].first(); + const nsDependentString& substitution = whitelistedPaths[i].second(); + if (StringBeginsWith(cleanPath, testPath, + nsCaseInsensitiveStringComparator())) { + if (!substitution.IsVoid()) { + cleanPath.Replace(0, testPath.Length(), substitution); + } + // Whitelisted paths may be used as-is provided that they have been + // previously sanitized. + needsStrip = false; + break; + } + } + if (!aOutput.IsEmpty()) { + aOutput += L";"; + } + // For non-whitelisted paths, we strip the path component and just leave + // the filename. + if (needsStrip) { + // nsLocalFile doesn't like non-absolute paths. Since these paths might + // contain environment variables instead of roots, we can't use it. + wchar_t tmpPath[MAX_PATH + 1] = {0}; + wcsncpy(tmpPath, cleanPath.get(), cleanPath.Length()); + PathStripPath(tmpPath); + aOutput += tmpPath; + } else { + aOutput += cleanPath; + } + } + token = wcstok_s(nullptr, kDelimiters, &tokenContext); + } + return true; +} + +} // namespace widget +} // namespace mozilla |