1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "WinUtils.h"
8 
9 #include <knownfolders.h>
10 #include <winioctl.h>
11 
12 #include "GeckoProfiler.h"
13 #include "gfxPlatform.h"
14 #include "gfxUtils.h"
15 #include "nsWindow.h"
16 #include "nsWindowDefs.h"
17 #include "InputDeviceUtils.h"
18 #include "KeyboardLayout.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/BackgroundHangMonitor.h"
21 #include "mozilla/ClearOnShutdown.h"
22 #include "mozilla/dom/MouseEventBinding.h"
23 #include "mozilla/gfx/2D.h"
24 #include "mozilla/gfx/DataSurfaceHelpers.h"
25 #include "mozilla/gfx/Logging.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/RefPtr.h"
28 #include "mozilla/SchedulerGroup.h"
29 #include "mozilla/WindowsVersion.h"
30 #include "mozilla/Unused.h"
31 #include "nsIContentPolicy.h"
32 #include "nsIWindowsUIUtils.h"
33 #include "nsContentUtils.h"
34 
35 #include "mozilla/Logging.h"
36 
37 #include "nsString.h"
38 #include "nsDirectoryServiceUtils.h"
39 #include "imgIContainer.h"
40 #include "imgITools.h"
41 #include "nsNetUtil.h"
42 #include "nsIOutputStream.h"
43 #include "nsNetCID.h"
44 #include "prtime.h"
45 #ifdef MOZ_PLACES
46 #  include "nsIFaviconService.h"
47 #endif
48 #include "nsIDownloader.h"
49 #include "nsIChannel.h"
50 #include "nsIThread.h"
51 #include "MainThreadUtils.h"
52 #include "nsLookAndFeel.h"
53 #include "nsUnicharUtils.h"
54 #include "nsWindowsHelpers.h"
55 #include "WinContentSystemParameters.h"
56 
57 #include <textstor.h>
58 #include "TSFTextStore.h"
59 
60 #include <shlobj.h>
61 #include <shlwapi.h>
62 
63 mozilla::LazyLogModule gWindowsLog("Widget");
64 
65 #define LOG_E(...) MOZ_LOG(gWindowsLog, LogLevel::Error, (__VA_ARGS__))
66 #define LOG_D(...) MOZ_LOG(gWindowsLog, LogLevel::Debug, (__VA_ARGS__))
67 
68 using namespace mozilla::gfx;
69 
70 namespace mozilla {
71 namespace widget {
72 
73 #define ENTRY(_msg) \
74   { #_msg, _msg }
75 EventMsgInfo gAllEvents[] = {ENTRY(WM_NULL),
76                              ENTRY(WM_CREATE),
77                              ENTRY(WM_DESTROY),
78                              ENTRY(WM_MOVE),
79                              ENTRY(WM_SIZE),
80                              ENTRY(WM_ACTIVATE),
81                              ENTRY(WM_SETFOCUS),
82                              ENTRY(WM_KILLFOCUS),
83                              ENTRY(WM_ENABLE),
84                              ENTRY(WM_SETREDRAW),
85                              ENTRY(WM_SETTEXT),
86                              ENTRY(WM_GETTEXT),
87                              ENTRY(WM_GETTEXTLENGTH),
88                              ENTRY(WM_PAINT),
89                              ENTRY(WM_CLOSE),
90                              ENTRY(WM_QUERYENDSESSION),
91                              ENTRY(WM_QUIT),
92                              ENTRY(WM_QUERYOPEN),
93                              ENTRY(WM_ERASEBKGND),
94                              ENTRY(WM_SYSCOLORCHANGE),
95                              ENTRY(WM_ENDSESSION),
96                              ENTRY(WM_SHOWWINDOW),
97                              ENTRY(WM_SETTINGCHANGE),
98                              ENTRY(WM_DEVMODECHANGE),
99                              ENTRY(WM_ACTIVATEAPP),
100                              ENTRY(WM_FONTCHANGE),
101                              ENTRY(WM_TIMECHANGE),
102                              ENTRY(WM_CANCELMODE),
103                              ENTRY(WM_SETCURSOR),
104                              ENTRY(WM_MOUSEACTIVATE),
105                              ENTRY(WM_CHILDACTIVATE),
106                              ENTRY(WM_QUEUESYNC),
107                              ENTRY(WM_GETMINMAXINFO),
108                              ENTRY(WM_PAINTICON),
109                              ENTRY(WM_ICONERASEBKGND),
110                              ENTRY(WM_NEXTDLGCTL),
111                              ENTRY(WM_SPOOLERSTATUS),
112                              ENTRY(WM_DRAWITEM),
113                              ENTRY(WM_MEASUREITEM),
114                              ENTRY(WM_DELETEITEM),
115                              ENTRY(WM_VKEYTOITEM),
116                              ENTRY(WM_CHARTOITEM),
117                              ENTRY(WM_SETFONT),
118                              ENTRY(WM_GETFONT),
119                              ENTRY(WM_SETHOTKEY),
120                              ENTRY(WM_GETHOTKEY),
121                              ENTRY(WM_QUERYDRAGICON),
122                              ENTRY(WM_COMPAREITEM),
123                              ENTRY(WM_GETOBJECT),
124                              ENTRY(WM_COMPACTING),
125                              ENTRY(WM_COMMNOTIFY),
126                              ENTRY(WM_WINDOWPOSCHANGING),
127                              ENTRY(WM_WINDOWPOSCHANGED),
128                              ENTRY(WM_POWER),
129                              ENTRY(WM_COPYDATA),
130                              ENTRY(WM_CANCELJOURNAL),
131                              ENTRY(WM_NOTIFY),
132                              ENTRY(WM_INPUTLANGCHANGEREQUEST),
133                              ENTRY(WM_INPUTLANGCHANGE),
134                              ENTRY(WM_TCARD),
135                              ENTRY(WM_HELP),
136                              ENTRY(WM_USERCHANGED),
137                              ENTRY(WM_NOTIFYFORMAT),
138                              ENTRY(WM_CONTEXTMENU),
139                              ENTRY(WM_STYLECHANGING),
140                              ENTRY(WM_STYLECHANGED),
141                              ENTRY(WM_DISPLAYCHANGE),
142                              ENTRY(WM_GETICON),
143                              ENTRY(WM_SETICON),
144                              ENTRY(WM_NCCREATE),
145                              ENTRY(WM_NCDESTROY),
146                              ENTRY(WM_NCCALCSIZE),
147                              ENTRY(WM_NCHITTEST),
148                              ENTRY(WM_NCPAINT),
149                              ENTRY(WM_NCACTIVATE),
150                              ENTRY(WM_GETDLGCODE),
151                              ENTRY(WM_SYNCPAINT),
152                              ENTRY(WM_NCMOUSEMOVE),
153                              ENTRY(WM_NCLBUTTONDOWN),
154                              ENTRY(WM_NCLBUTTONUP),
155                              ENTRY(WM_NCLBUTTONDBLCLK),
156                              ENTRY(WM_NCRBUTTONDOWN),
157                              ENTRY(WM_NCRBUTTONUP),
158                              ENTRY(WM_NCRBUTTONDBLCLK),
159                              ENTRY(WM_NCMBUTTONDOWN),
160                              ENTRY(WM_NCMBUTTONUP),
161                              ENTRY(WM_NCMBUTTONDBLCLK),
162                              ENTRY(EM_GETSEL),
163                              ENTRY(EM_SETSEL),
164                              ENTRY(EM_GETRECT),
165                              ENTRY(EM_SETRECT),
166                              ENTRY(EM_SETRECTNP),
167                              ENTRY(EM_SCROLL),
168                              ENTRY(EM_LINESCROLL),
169                              ENTRY(EM_SCROLLCARET),
170                              ENTRY(EM_GETMODIFY),
171                              ENTRY(EM_SETMODIFY),
172                              ENTRY(EM_GETLINECOUNT),
173                              ENTRY(EM_LINEINDEX),
174                              ENTRY(EM_SETHANDLE),
175                              ENTRY(EM_GETHANDLE),
176                              ENTRY(EM_GETTHUMB),
177                              ENTRY(EM_LINELENGTH),
178                              ENTRY(EM_REPLACESEL),
179                              ENTRY(EM_GETLINE),
180                              ENTRY(EM_LIMITTEXT),
181                              ENTRY(EM_CANUNDO),
182                              ENTRY(EM_UNDO),
183                              ENTRY(EM_FMTLINES),
184                              ENTRY(EM_LINEFROMCHAR),
185                              ENTRY(EM_SETTABSTOPS),
186                              ENTRY(EM_SETPASSWORDCHAR),
187                              ENTRY(EM_EMPTYUNDOBUFFER),
188                              ENTRY(EM_GETFIRSTVISIBLELINE),
189                              ENTRY(EM_SETREADONLY),
190                              ENTRY(EM_SETWORDBREAKPROC),
191                              ENTRY(EM_GETWORDBREAKPROC),
192                              ENTRY(EM_GETPASSWORDCHAR),
193                              ENTRY(EM_SETMARGINS),
194                              ENTRY(EM_GETMARGINS),
195                              ENTRY(EM_GETLIMITTEXT),
196                              ENTRY(EM_POSFROMCHAR),
197                              ENTRY(EM_CHARFROMPOS),
198                              ENTRY(EM_SETIMESTATUS),
199                              ENTRY(EM_GETIMESTATUS),
200                              ENTRY(SBM_SETPOS),
201                              ENTRY(SBM_GETPOS),
202                              ENTRY(SBM_SETRANGE),
203                              ENTRY(SBM_SETRANGEREDRAW),
204                              ENTRY(SBM_GETRANGE),
205                              ENTRY(SBM_ENABLE_ARROWS),
206                              ENTRY(SBM_SETSCROLLINFO),
207                              ENTRY(SBM_GETSCROLLINFO),
208                              ENTRY(WM_KEYDOWN),
209                              ENTRY(WM_KEYUP),
210                              ENTRY(WM_CHAR),
211                              ENTRY(WM_DEADCHAR),
212                              ENTRY(WM_SYSKEYDOWN),
213                              ENTRY(WM_SYSKEYUP),
214                              ENTRY(WM_SYSCHAR),
215                              ENTRY(WM_SYSDEADCHAR),
216                              ENTRY(WM_KEYLAST),
217                              ENTRY(WM_IME_STARTCOMPOSITION),
218                              ENTRY(WM_IME_ENDCOMPOSITION),
219                              ENTRY(WM_IME_COMPOSITION),
220                              ENTRY(WM_INITDIALOG),
221                              ENTRY(WM_COMMAND),
222                              ENTRY(WM_SYSCOMMAND),
223                              ENTRY(WM_TIMER),
224                              ENTRY(WM_HSCROLL),
225                              ENTRY(WM_VSCROLL),
226                              ENTRY(WM_INITMENU),
227                              ENTRY(WM_INITMENUPOPUP),
228                              ENTRY(WM_MENUSELECT),
229                              ENTRY(WM_MENUCHAR),
230                              ENTRY(WM_ENTERIDLE),
231                              ENTRY(WM_MENURBUTTONUP),
232                              ENTRY(WM_MENUDRAG),
233                              ENTRY(WM_MENUGETOBJECT),
234                              ENTRY(WM_UNINITMENUPOPUP),
235                              ENTRY(WM_MENUCOMMAND),
236                              ENTRY(WM_CHANGEUISTATE),
237                              ENTRY(WM_UPDATEUISTATE),
238                              ENTRY(WM_CTLCOLORMSGBOX),
239                              ENTRY(WM_CTLCOLOREDIT),
240                              ENTRY(WM_CTLCOLORLISTBOX),
241                              ENTRY(WM_CTLCOLORBTN),
242                              ENTRY(WM_CTLCOLORDLG),
243                              ENTRY(WM_CTLCOLORSCROLLBAR),
244                              ENTRY(WM_CTLCOLORSTATIC),
245                              ENTRY(CB_GETEDITSEL),
246                              ENTRY(CB_LIMITTEXT),
247                              ENTRY(CB_SETEDITSEL),
248                              ENTRY(CB_ADDSTRING),
249                              ENTRY(CB_DELETESTRING),
250                              ENTRY(CB_DIR),
251                              ENTRY(CB_GETCOUNT),
252                              ENTRY(CB_GETCURSEL),
253                              ENTRY(CB_GETLBTEXT),
254                              ENTRY(CB_GETLBTEXTLEN),
255                              ENTRY(CB_INSERTSTRING),
256                              ENTRY(CB_RESETCONTENT),
257                              ENTRY(CB_FINDSTRING),
258                              ENTRY(CB_SELECTSTRING),
259                              ENTRY(CB_SETCURSEL),
260                              ENTRY(CB_SHOWDROPDOWN),
261                              ENTRY(CB_GETITEMDATA),
262                              ENTRY(CB_SETITEMDATA),
263                              ENTRY(CB_GETDROPPEDCONTROLRECT),
264                              ENTRY(CB_SETITEMHEIGHT),
265                              ENTRY(CB_GETITEMHEIGHT),
266                              ENTRY(CB_SETEXTENDEDUI),
267                              ENTRY(CB_GETEXTENDEDUI),
268                              ENTRY(CB_GETDROPPEDSTATE),
269                              ENTRY(CB_FINDSTRINGEXACT),
270                              ENTRY(CB_SETLOCALE),
271                              ENTRY(CB_GETLOCALE),
272                              ENTRY(CB_GETTOPINDEX),
273                              ENTRY(CB_SETTOPINDEX),
274                              ENTRY(CB_GETHORIZONTALEXTENT),
275                              ENTRY(CB_SETHORIZONTALEXTENT),
276                              ENTRY(CB_GETDROPPEDWIDTH),
277                              ENTRY(CB_SETDROPPEDWIDTH),
278                              ENTRY(CB_INITSTORAGE),
279                              ENTRY(CB_MSGMAX),
280                              ENTRY(LB_ADDSTRING),
281                              ENTRY(LB_INSERTSTRING),
282                              ENTRY(LB_DELETESTRING),
283                              ENTRY(LB_SELITEMRANGEEX),
284                              ENTRY(LB_RESETCONTENT),
285                              ENTRY(LB_SETSEL),
286                              ENTRY(LB_SETCURSEL),
287                              ENTRY(LB_GETSEL),
288                              ENTRY(LB_GETCURSEL),
289                              ENTRY(LB_GETTEXT),
290                              ENTRY(LB_GETTEXTLEN),
291                              ENTRY(LB_GETCOUNT),
292                              ENTRY(LB_SELECTSTRING),
293                              ENTRY(LB_DIR),
294                              ENTRY(LB_GETTOPINDEX),
295                              ENTRY(LB_FINDSTRING),
296                              ENTRY(LB_GETSELCOUNT),
297                              ENTRY(LB_GETSELITEMS),
298                              ENTRY(LB_SETTABSTOPS),
299                              ENTRY(LB_GETHORIZONTALEXTENT),
300                              ENTRY(LB_SETHORIZONTALEXTENT),
301                              ENTRY(LB_SETCOLUMNWIDTH),
302                              ENTRY(LB_ADDFILE),
303                              ENTRY(LB_SETTOPINDEX),
304                              ENTRY(LB_GETITEMRECT),
305                              ENTRY(LB_GETITEMDATA),
306                              ENTRY(LB_SETITEMDATA),
307                              ENTRY(LB_SELITEMRANGE),
308                              ENTRY(LB_SETANCHORINDEX),
309                              ENTRY(LB_GETANCHORINDEX),
310                              ENTRY(LB_SETCARETINDEX),
311                              ENTRY(LB_GETCARETINDEX),
312                              ENTRY(LB_SETITEMHEIGHT),
313                              ENTRY(LB_GETITEMHEIGHT),
314                              ENTRY(LB_FINDSTRINGEXACT),
315                              ENTRY(LB_SETLOCALE),
316                              ENTRY(LB_GETLOCALE),
317                              ENTRY(LB_SETCOUNT),
318                              ENTRY(LB_INITSTORAGE),
319                              ENTRY(LB_ITEMFROMPOINT),
320                              ENTRY(LB_MSGMAX),
321                              ENTRY(WM_MOUSEMOVE),
322                              ENTRY(WM_LBUTTONDOWN),
323                              ENTRY(WM_LBUTTONUP),
324                              ENTRY(WM_LBUTTONDBLCLK),
325                              ENTRY(WM_RBUTTONDOWN),
326                              ENTRY(WM_RBUTTONUP),
327                              ENTRY(WM_RBUTTONDBLCLK),
328                              ENTRY(WM_MBUTTONDOWN),
329                              ENTRY(WM_MBUTTONUP),
330                              ENTRY(WM_MBUTTONDBLCLK),
331                              ENTRY(WM_MOUSEWHEEL),
332                              ENTRY(WM_MOUSEHWHEEL),
333                              ENTRY(WM_PARENTNOTIFY),
334                              ENTRY(WM_ENTERMENULOOP),
335                              ENTRY(WM_EXITMENULOOP),
336                              ENTRY(WM_NEXTMENU),
337                              ENTRY(WM_SIZING),
338                              ENTRY(WM_CAPTURECHANGED),
339                              ENTRY(WM_MOVING),
340                              ENTRY(WM_POWERBROADCAST),
341                              ENTRY(WM_DEVICECHANGE),
342                              ENTRY(WM_MDICREATE),
343                              ENTRY(WM_MDIDESTROY),
344                              ENTRY(WM_MDIACTIVATE),
345                              ENTRY(WM_MDIRESTORE),
346                              ENTRY(WM_MDINEXT),
347                              ENTRY(WM_MDIMAXIMIZE),
348                              ENTRY(WM_MDITILE),
349                              ENTRY(WM_MDICASCADE),
350                              ENTRY(WM_MDIICONARRANGE),
351                              ENTRY(WM_MDIGETACTIVE),
352                              ENTRY(WM_MDISETMENU),
353                              ENTRY(WM_ENTERSIZEMOVE),
354                              ENTRY(WM_EXITSIZEMOVE),
355                              ENTRY(WM_DROPFILES),
356                              ENTRY(WM_MDIREFRESHMENU),
357                              ENTRY(WM_IME_SETCONTEXT),
358                              ENTRY(WM_IME_NOTIFY),
359                              ENTRY(WM_IME_CONTROL),
360                              ENTRY(WM_IME_COMPOSITIONFULL),
361                              ENTRY(WM_IME_SELECT),
362                              ENTRY(WM_IME_CHAR),
363                              ENTRY(WM_IME_REQUEST),
364                              ENTRY(WM_IME_KEYDOWN),
365                              ENTRY(WM_IME_KEYUP),
366                              ENTRY(WM_NCMOUSEHOVER),
367                              ENTRY(WM_MOUSEHOVER),
368                              ENTRY(WM_MOUSELEAVE),
369                              ENTRY(WM_CUT),
370                              ENTRY(WM_COPY),
371                              ENTRY(WM_PASTE),
372                              ENTRY(WM_CLEAR),
373                              ENTRY(WM_UNDO),
374                              ENTRY(WM_RENDERFORMAT),
375                              ENTRY(WM_RENDERALLFORMATS),
376                              ENTRY(WM_DESTROYCLIPBOARD),
377                              ENTRY(WM_DRAWCLIPBOARD),
378                              ENTRY(WM_PAINTCLIPBOARD),
379                              ENTRY(WM_VSCROLLCLIPBOARD),
380                              ENTRY(WM_SIZECLIPBOARD),
381                              ENTRY(WM_ASKCBFORMATNAME),
382                              ENTRY(WM_CHANGECBCHAIN),
383                              ENTRY(WM_HSCROLLCLIPBOARD),
384                              ENTRY(WM_QUERYNEWPALETTE),
385                              ENTRY(WM_PALETTEISCHANGING),
386                              ENTRY(WM_PALETTECHANGED),
387                              ENTRY(WM_HOTKEY),
388                              ENTRY(WM_PRINT),
389                              ENTRY(WM_PRINTCLIENT),
390                              ENTRY(WM_THEMECHANGED),
391                              ENTRY(WM_HANDHELDFIRST),
392                              ENTRY(WM_HANDHELDLAST),
393                              ENTRY(WM_AFXFIRST),
394                              ENTRY(WM_AFXLAST),
395                              ENTRY(WM_PENWINFIRST),
396                              ENTRY(WM_PENWINLAST),
397                              ENTRY(WM_APP),
398                              ENTRY(WM_DWMCOMPOSITIONCHANGED),
399                              ENTRY(WM_DWMNCRENDERINGCHANGED),
400                              ENTRY(WM_DWMCOLORIZATIONCOLORCHANGED),
401                              ENTRY(WM_DWMWINDOWMAXIMIZEDCHANGE),
402                              ENTRY(WM_DWMSENDICONICTHUMBNAIL),
403                              ENTRY(WM_DWMSENDICONICLIVEPREVIEWBITMAP),
404                              ENTRY(WM_TABLET_QUERYSYSTEMGESTURESTATUS),
405                              ENTRY(WM_GESTURE),
406                              ENTRY(WM_GESTURENOTIFY),
407                              ENTRY(WM_GETTITLEBARINFOEX),
408                              {nullptr, 0x0}};
409 #undef ENTRY
410 
411 #ifdef MOZ_PLACES
412 NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
413 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
414 #endif
415 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
416 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
417 
418 const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
419 const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
420 
421 // Prefix for path used by NT calls.
422 const wchar_t kNTPrefix[] = L"\\??\\";
423 const size_t kNTPrefixLen = ArrayLength(kNTPrefix) - 1;
424 
425 struct CoTaskMemFreePolicy {
operator ()mozilla::widget::CoTaskMemFreePolicy426   void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
427 };
428 
429 SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
430 EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
431 GetSystemMetricsForDpiProc WinUtils::sGetSystemMetricsForDpi = NULL;
432 
433 /* static */
Initialize()434 void WinUtils::Initialize() {
435   // Dpi-Awareness is not supported with Win32k Lockdown enabled, so we don't
436   // initialize DPI-related members and assert later that nothing accidently
437   // uses these static members
438   if (IsWin10OrLater() && !IsWin32kLockedDown()) {
439     HMODULE user32Dll = ::GetModuleHandleW(L"user32");
440     if (user32Dll) {
441       auto getThreadDpiAwarenessContext =
442           (decltype(GetThreadDpiAwarenessContext)*)::GetProcAddress(
443               user32Dll, "GetThreadDpiAwarenessContext");
444       auto areDpiAwarenessContextsEqual =
445           (decltype(AreDpiAwarenessContextsEqual)*)::GetProcAddress(
446               user32Dll, "AreDpiAwarenessContextsEqual");
447       if (getThreadDpiAwarenessContext && areDpiAwarenessContextsEqual &&
448           areDpiAwarenessContextsEqual(
449               getThreadDpiAwarenessContext(),
450               DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
451         // Only per-monitor v1 requires these workarounds.
452         sEnableNonClientDpiScaling =
453             (EnableNonClientDpiScalingProc)::GetProcAddress(
454                 user32Dll, "EnableNonClientDpiScaling");
455         sSetThreadDpiAwarenessContext =
456             (SetThreadDpiAwarenessContextProc)::GetProcAddress(
457                 user32Dll, "SetThreadDpiAwarenessContext");
458       }
459 
460       sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)::GetProcAddress(
461           user32Dll, "GetSystemMetricsForDpi");
462     }
463   }
464 }
465 
466 // static
NonClientDpiScalingDefWindowProcW(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)467 LRESULT WINAPI WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg,
468                                                            WPARAM wParam,
469                                                            LPARAM lParam) {
470   MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
471 
472   // NOTE: this function was copied out into the body of the pre-XUL skeleton
473   // UI window proc (PreXULSkeletonUI.cpp). If this function changes at any
474   // point, we should probably factor this out and use it from both locations.
475   if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) {
476     sEnableNonClientDpiScaling(hWnd);
477   }
478   return ::DefWindowProcW(hWnd, msg, wParam, lParam);
479 }
480 
481 // static
LogW(const wchar_t * fmt,...)482 void WinUtils::LogW(const wchar_t* fmt, ...) {
483   va_list args = nullptr;
484   if (!lstrlenW(fmt)) {
485     return;
486   }
487   va_start(args, fmt);
488   int buflen = _vscwprintf(fmt, args);
489   wchar_t* buffer = new wchar_t[buflen + 1];
490   if (!buffer) {
491     va_end(args);
492     return;
493   }
494   vswprintf(buffer, buflen, fmt, args);
495   va_end(args);
496 
497   // MSVC, including remote debug sessions
498   OutputDebugStringW(buffer);
499   OutputDebugStringW(L"\n");
500 
501   int len =
502       WideCharToMultiByte(CP_ACP, 0, buffer, -1, nullptr, 0, nullptr, nullptr);
503   if (len) {
504     char* utf8 = new char[len];
505     if (WideCharToMultiByte(CP_ACP, 0, buffer, -1, utf8, len, nullptr,
506                             nullptr) > 0) {
507       // desktop console
508       printf("%s\n", utf8);
509       NS_ASSERTION(gWindowsLog,
510                    "Called WinUtils Log() but Widget "
511                    "log module doesn't exist!");
512       MOZ_LOG(gWindowsLog, LogLevel::Error, (utf8));
513     }
514     delete[] utf8;
515   }
516   delete[] buffer;
517 }
518 
519 // static
Log(const char * fmt,...)520 void WinUtils::Log(const char* fmt, ...) {
521   va_list args = nullptr;
522   if (!strlen(fmt)) {
523     return;
524   }
525   va_start(args, fmt);
526   int buflen = _vscprintf(fmt, args);
527   char* buffer = new char[buflen + 1];
528   if (!buffer) {
529     va_end(args);
530     return;
531   }
532   vsprintf(buffer, fmt, args);
533   va_end(args);
534 
535   // MSVC, including remote debug sessions
536   OutputDebugStringA(buffer);
537   OutputDebugStringW(L"\n");
538 
539   // desktop console
540   printf("%s\n", buffer);
541 
542   NS_ASSERTION(gWindowsLog,
543                "Called WinUtils Log() but Widget "
544                "log module doesn't exist!");
545   MOZ_LOG(gWindowsLog, LogLevel::Error, (buffer));
546   delete[] buffer;
547 }
548 
549 // static
SystemDPI()550 float WinUtils::SystemDPI() {
551   if (XRE_IsContentProcess()) {
552     return WinContentSystemParameters::GetSingleton()->SystemDPI();
553   }
554   // The result of GetDeviceCaps won't change dynamically, as it predates
555   // per-monitor DPI and support for on-the-fly resolution changes.
556   // Therefore, we only need to look it up once.
557   static float dpi = 0;
558   if (dpi <= 0) {
559     HDC screenDC = GetDC(nullptr);
560     dpi = GetDeviceCaps(screenDC, LOGPIXELSY);
561     ReleaseDC(nullptr, screenDC);
562   }
563 
564   // Bug 1012487 - dpi can be 0 when the Screen DC is used off the
565   // main thread on windows. For now just assume a 100% DPI for this
566   // drawing call.
567   // XXX - fixme!
568   return dpi > 0 ? dpi : 96;
569 }
570 
571 // static
SystemScaleFactor()572 double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }
573 
574 #if WINVER < 0x603
575 typedef enum {
576   MDT_EFFECTIVE_DPI = 0,
577   MDT_ANGULAR_DPI = 1,
578   MDT_RAW_DPI = 2,
579   MDT_DEFAULT = MDT_EFFECTIVE_DPI
580 } MONITOR_DPI_TYPE;
581 
582 typedef enum {
583   PROCESS_DPI_UNAWARE = 0,
584   PROCESS_SYSTEM_DPI_AWARE = 1,
585   PROCESS_PER_MONITOR_DPI_AWARE = 2
586 } PROCESS_DPI_AWARENESS;
587 #endif
588 
589 typedef HRESULT(WINAPI* GETDPIFORMONITORPROC)(HMONITOR, MONITOR_DPI_TYPE, UINT*,
590                                               UINT*);
591 
592 typedef HRESULT(WINAPI* GETPROCESSDPIAWARENESSPROC)(HANDLE,
593                                                     PROCESS_DPI_AWARENESS*);
594 
595 GETDPIFORMONITORPROC sGetDpiForMonitor;
596 GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness;
597 
SlowIsPerMonitorDPIAware()598 static bool SlowIsPerMonitorDPIAware() {
599   // Intentionally leak the handle.
600   HMODULE shcore = LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
601   if (shcore) {
602     sGetDpiForMonitor =
603         (GETDPIFORMONITORPROC)GetProcAddress(shcore, "GetDpiForMonitor");
604     sGetProcessDpiAwareness = (GETPROCESSDPIAWARENESSPROC)GetProcAddress(
605         shcore, "GetProcessDpiAwareness");
606   }
607   PROCESS_DPI_AWARENESS dpiAwareness;
608   return sGetDpiForMonitor && sGetProcessDpiAwareness &&
609          SUCCEEDED(
610              sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness)) &&
611          dpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE;
612 }
613 
614 /* static */
IsPerMonitorDPIAware()615 bool WinUtils::IsPerMonitorDPIAware() {
616   if (XRE_IsContentProcess()) {
617     return WinContentSystemParameters::GetSingleton()->IsPerMonitorDPIAware();
618   }
619   static bool perMonitorDPIAware = SlowIsPerMonitorDPIAware();
620   return perMonitorDPIAware;
621 }
622 
623 /* static */
MonitorDPI(HMONITOR aMonitor)624 float WinUtils::MonitorDPI(HMONITOR aMonitor) {
625   if (IsPerMonitorDPIAware()) {
626     UINT dpiX, dpiY = 96;
627     sGetDpiForMonitor(aMonitor ? aMonitor : GetPrimaryMonitor(),
628                       MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
629     return dpiY;
630   }
631 
632   // We're not per-monitor aware, use system DPI instead.
633   return SystemDPI();
634 }
635 
636 /* static */
LogToPhysFactor(HMONITOR aMonitor)637 double WinUtils::LogToPhysFactor(HMONITOR aMonitor) {
638   return MonitorDPI(aMonitor) / 96.0;
639 }
640 
641 /* static */
LogToPhys(HMONITOR aMonitor,double aValue)642 int32_t WinUtils::LogToPhys(HMONITOR aMonitor, double aValue) {
643   return int32_t(NS_round(aValue * LogToPhysFactor(aMonitor)));
644 }
645 
646 /* static */
647 HMONITOR
GetPrimaryMonitor()648 WinUtils::GetPrimaryMonitor() {
649   const POINT pt = {0, 0};
650   return ::MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
651 }
652 
653 /* static */
654 HMONITOR
MonitorFromRect(const gfx::Rect & rect)655 WinUtils::MonitorFromRect(const gfx::Rect& rect) {
656   // convert coordinates from desktop to device pixels for MonitorFromRect
657   double dpiScale =
658       IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor());
659 
660   RECT globalWindowBounds = {NSToIntRound(dpiScale * rect.X()),
661                              NSToIntRound(dpiScale * rect.Y()),
662                              NSToIntRound(dpiScale * (rect.XMost())),
663                              NSToIntRound(dpiScale * (rect.YMost()))};
664 
665   return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST);
666 }
667 
668 /* static */
HasSystemMetricsForDpi()669 bool WinUtils::HasSystemMetricsForDpi() {
670   MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
671   return (sGetSystemMetricsForDpi != NULL);
672 }
673 
674 /* static */
GetSystemMetricsForDpi(int nIndex,UINT dpi)675 int WinUtils::GetSystemMetricsForDpi(int nIndex, UINT dpi) {
676   MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
677   if (HasSystemMetricsForDpi()) {
678     return sGetSystemMetricsForDpi(nIndex, dpi);
679   } else {
680     double scale = IsPerMonitorDPIAware() ? dpi / SystemDPI() : 1.0;
681     return NSToIntRound(::GetSystemMetrics(nIndex) * scale);
682   }
683 }
684 
685 /* static */
GetUnwriteableMarginsForDeviceInInches(HDC aHdc)686 gfx::MarginDouble WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC aHdc) {
687   if (!aHdc) {
688     return gfx::MarginDouble();
689   }
690 
691   int pixelsPerInchY = ::GetDeviceCaps(aHdc, LOGPIXELSY);
692   int marginTop = ::GetDeviceCaps(aHdc, PHYSICALOFFSETY);
693   int printableAreaHeight = ::GetDeviceCaps(aHdc, VERTRES);
694   int physicalHeight = ::GetDeviceCaps(aHdc, PHYSICALHEIGHT);
695 
696   double marginTopInch = double(marginTop) / pixelsPerInchY;
697 
698   double printableAreaHeightInch = double(printableAreaHeight) / pixelsPerInchY;
699   double physicalHeightInch = double(physicalHeight) / pixelsPerInchY;
700   double marginBottomInch =
701       physicalHeightInch - printableAreaHeightInch - marginTopInch;
702 
703   int pixelsPerInchX = ::GetDeviceCaps(aHdc, LOGPIXELSX);
704   int marginLeft = ::GetDeviceCaps(aHdc, PHYSICALOFFSETX);
705   int printableAreaWidth = ::GetDeviceCaps(aHdc, HORZRES);
706   int physicalWidth = ::GetDeviceCaps(aHdc, PHYSICALWIDTH);
707 
708   double marginLeftInch = double(marginLeft) / pixelsPerInchX;
709 
710   double printableAreaWidthInch = double(printableAreaWidth) / pixelsPerInchX;
711   double physicalWidthInch = double(physicalWidth) / pixelsPerInchX;
712   double marginRightInch =
713       physicalWidthInch - printableAreaWidthInch - marginLeftInch;
714 
715   return gfx::MarginDouble(marginTopInch, marginRightInch, marginBottomInch,
716                            marginLeftInch);
717 }
718 
719 #ifdef ACCESSIBILITY
720 /* static */
GetRootAccessibleForHWND(HWND aHwnd)721 a11y::LocalAccessible* WinUtils::GetRootAccessibleForHWND(HWND aHwnd) {
722   nsWindow* window = GetNSWindowPtr(aHwnd);
723   if (!window) {
724     return nullptr;
725   }
726 
727   return window->GetAccessible();
728 }
729 #endif  // ACCESSIBILITY
730 
731 /* static */
PeekMessage(LPMSG aMsg,HWND aWnd,UINT aFirstMessage,UINT aLastMessage,UINT aOption)732 bool WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
733                            UINT aLastMessage, UINT aOption) {
734   RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
735   if (msgPump) {
736     BOOL ret = FALSE;
737     HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
738                                        aOption, &ret);
739     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
740     return ret;
741   }
742   return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption);
743 }
744 
745 /* static */
GetMessage(LPMSG aMsg,HWND aWnd,UINT aFirstMessage,UINT aLastMessage)746 bool WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
747                           UINT aLastMessage) {
748   RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
749   if (msgPump) {
750     BOOL ret = FALSE;
751     HRESULT hr =
752         msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, &ret);
753     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
754     return ret;
755   }
756   return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
757 }
758 
759 #if defined(ACCESSIBILITY)
GetWaitFlags()760 static DWORD GetWaitFlags() {
761   DWORD result = MWMO_INPUTAVAILABLE;
762   if (XRE_IsContentProcess()) {
763     result |= MWMO_ALERTABLE;
764   }
765   return result;
766 }
767 #endif
768 
769 /* static */
WaitForMessage(DWORD aTimeoutMs)770 void WinUtils::WaitForMessage(DWORD aTimeoutMs) {
771 #if defined(ACCESSIBILITY)
772   static const DWORD waitFlags = GetWaitFlags();
773 #else
774   const DWORD waitFlags = MWMO_INPUTAVAILABLE;
775 #endif
776 
777   const DWORD waitStart = ::GetTickCount();
778   DWORD elapsed = 0;
779   while (true) {
780     if (aTimeoutMs != INFINITE) {
781       elapsed = ::GetTickCount() - waitStart;
782     }
783     if (elapsed >= aTimeoutMs) {
784       break;
785     }
786     DWORD result;
787     {
788       AUTO_PROFILER_THREAD_SLEEP;
789       result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
790                                              MOZ_QS_ALLEVENT, waitFlags);
791     }
792     NS_WARNING_ASSERTION(result != WAIT_FAILED, "Wait failed");
793     if (result == WAIT_TIMEOUT) {
794       break;
795     }
796 #if defined(ACCESSIBILITY)
797     if (result == WAIT_IO_COMPLETION) {
798       if (NS_IsMainThread()) {
799         // We executed an APC that would have woken up the hang monitor. Since
800         // there are no more APCs pending and we are now going to sleep again,
801         // we should notify the hang monitor.
802         mozilla::BackgroundHangMonitor().NotifyWait();
803       }
804       continue;
805     }
806 #endif  // defined(ACCESSIBILITY)
807 
808     // Sent messages (via SendMessage and friends) are processed differently
809     // than queued messages (via PostMessage); the destination window procedure
810     // of the sent message is called during (Get|Peek)Message. Since PeekMessage
811     // does not tell us whether it processed any sent messages, we need to query
812     // this ahead of time.
813     bool haveSentMessagesPending =
814         (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
815 
816     MSG msg = {0};
817     if (haveSentMessagesPending ||
818         ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
819       break;
820     }
821     // The message is intended for another thread that has been synchronized
822     // with our input queue; yield to give other threads an opportunity to
823     // process the message. This should prevent busy waiting if resumed due
824     // to another thread's message.
825     ::SwitchToThread();
826   }
827 }
828 
829 /* static */
GetRegistryKey(HKEY aRoot,char16ptr_t aKeyName,char16ptr_t aValueName,wchar_t * aBuffer,DWORD aBufferLength)830 bool WinUtils::GetRegistryKey(HKEY aRoot, char16ptr_t aKeyName,
831                               char16ptr_t aValueName, wchar_t* aBuffer,
832                               DWORD aBufferLength) {
833   MOZ_ASSERT(aKeyName, "The key name is NULL");
834 
835   HKEY key;
836   LONG result =
837       ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
838   if (result != ERROR_SUCCESS) {
839     result =
840         ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
841     if (result != ERROR_SUCCESS) {
842       return false;
843     }
844   }
845 
846   DWORD type;
847   result = ::RegQueryValueExW(key, aValueName, nullptr, &type, (BYTE*)aBuffer,
848                               &aBufferLength);
849   ::RegCloseKey(key);
850   if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
851     return false;
852   }
853   if (aBuffer) {
854     aBuffer[aBufferLength / sizeof(*aBuffer) - 1] = 0;
855   }
856   return true;
857 }
858 
859 /* static */
HasRegistryKey(HKEY aRoot,char16ptr_t aKeyName)860 bool WinUtils::HasRegistryKey(HKEY aRoot, char16ptr_t aKeyName) {
861   MOZ_ASSERT(aRoot, "aRoot must not be NULL");
862   MOZ_ASSERT(aKeyName, "aKeyName must not be NULL");
863   HKEY key;
864   LONG result =
865       ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
866   if (result != ERROR_SUCCESS) {
867     result =
868         ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
869     if (result != ERROR_SUCCESS) {
870       return false;
871     }
872   }
873   ::RegCloseKey(key);
874   return true;
875 }
876 
877 /* static */
GetTopLevelHWND(HWND aWnd,bool aStopIfNotChild,bool aStopIfNotPopup)878 HWND WinUtils::GetTopLevelHWND(HWND aWnd, bool aStopIfNotChild,
879                                bool aStopIfNotPopup) {
880   HWND curWnd = aWnd;
881   HWND topWnd = nullptr;
882 
883   while (curWnd) {
884     topWnd = curWnd;
885 
886     if (aStopIfNotChild) {
887       DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE);
888 
889       VERIFY_WINDOW_STYLE(style);
890 
891       if (!(style & WS_CHILD))  // first top-level window
892         break;
893     }
894 
895     HWND upWnd = ::GetParent(curWnd);  // Parent or owner (if has no parent)
896 
897     // GetParent will only return the owner if the passed in window
898     // has the WS_POPUP style.
899     if (!upWnd && !aStopIfNotPopup) {
900       upWnd = ::GetWindow(curWnd, GW_OWNER);
901     }
902     curWnd = upWnd;
903   }
904 
905   return topWnd;
906 }
907 
GetNSWindowPropName()908 static const wchar_t* GetNSWindowPropName() {
909   static wchar_t sPropName[40] = L"";
910   if (!*sPropName) {
911     _snwprintf(sPropName, 39, L"MozillansIWidgetPtr%u",
912                ::GetCurrentProcessId());
913     sPropName[39] = '\0';
914   }
915   return sPropName;
916 }
917 
918 /* static */
SetNSWindowBasePtr(HWND aWnd,nsWindowBase * aWidget)919 bool WinUtils::SetNSWindowBasePtr(HWND aWnd, nsWindowBase* aWidget) {
920   if (!aWidget) {
921     ::RemovePropW(aWnd, GetNSWindowPropName());
922     return true;
923   }
924   return ::SetPropW(aWnd, GetNSWindowPropName(), (HANDLE)aWidget);
925 }
926 
927 /* static */
GetNSWindowBasePtr(HWND aWnd)928 nsWindowBase* WinUtils::GetNSWindowBasePtr(HWND aWnd) {
929   return static_cast<nsWindowBase*>(::GetPropW(aWnd, GetNSWindowPropName()));
930 }
931 
932 /* static */
GetNSWindowPtr(HWND aWnd)933 nsWindow* WinUtils::GetNSWindowPtr(HWND aWnd) {
934   return static_cast<nsWindow*>(::GetPropW(aWnd, GetNSWindowPropName()));
935 }
936 
AddMonitor(HMONITOR,HDC,LPRECT,LPARAM aParam)937 static BOOL CALLBACK AddMonitor(HMONITOR, HDC, LPRECT, LPARAM aParam) {
938   (*(int32_t*)aParam)++;
939   return TRUE;
940 }
941 
942 /* static */
GetMonitorCount()943 int32_t WinUtils::GetMonitorCount() {
944   int32_t monitorCount = 0;
945   EnumDisplayMonitors(nullptr, nullptr, AddMonitor, (LPARAM)&monitorCount);
946   return monitorCount;
947 }
948 
949 /* static */
IsOurProcessWindow(HWND aWnd)950 bool WinUtils::IsOurProcessWindow(HWND aWnd) {
951   if (!aWnd) {
952     return false;
953   }
954   DWORD processId = 0;
955   ::GetWindowThreadProcessId(aWnd, &processId);
956   return (processId == ::GetCurrentProcessId());
957 }
958 
959 /* static */
FindOurProcessWindow(HWND aWnd)960 HWND WinUtils::FindOurProcessWindow(HWND aWnd) {
961   for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) {
962     if (IsOurProcessWindow(wnd)) {
963       return wnd;
964     }
965   }
966   return nullptr;
967 }
968 
IsPointInWindow(HWND aWnd,const POINT & aPointInScreen)969 static bool IsPointInWindow(HWND aWnd, const POINT& aPointInScreen) {
970   RECT bounds;
971   if (!::GetWindowRect(aWnd, &bounds)) {
972     return false;
973   }
974 
975   return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right &&
976           aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom);
977 }
978 
979 /**
980  * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
981  * forground in this context) of aWnd.
982  */
983 
FindTopmostWindowAtPoint(HWND aWnd,const POINT & aPointInScreen)984 static HWND FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen) {
985   if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) {
986     return nullptr;
987   }
988 
989   HWND childWnd = ::GetTopWindow(aWnd);
990   while (childWnd) {
991     HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen);
992     if (topmostWnd) {
993       return topmostWnd;
994     }
995     childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
996   }
997 
998   return aWnd;
999 }
1000 
1001 struct FindOurWindowAtPointInfo {
1002   POINT mInPointInScreen;
1003   HWND mOutWnd;
1004 };
1005 
FindOurWindowAtPointCallback(HWND aWnd,LPARAM aLPARAM)1006 static BOOL CALLBACK FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM) {
1007   if (!WinUtils::IsOurProcessWindow(aWnd)) {
1008     // This isn't one of our top-level windows; continue enumerating.
1009     return TRUE;
1010   }
1011 
1012   // Get the top-most child window under the point.  If there's no child
1013   // window, and the point is within the top-level window, then the top-level
1014   // window will be returned.  (This is the usual case.  A child window
1015   // would be returned for plugins.)
1016   FindOurWindowAtPointInfo* info =
1017       reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
1018   HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen);
1019   if (!childWnd) {
1020     // This window doesn't contain the point; continue enumerating.
1021     return TRUE;
1022   }
1023 
1024   // Return the HWND and stop enumerating.
1025   info->mOutWnd = childWnd;
1026   return FALSE;
1027 }
1028 
1029 /* static */
FindOurWindowAtPoint(const POINT & aPointInScreen)1030 HWND WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen) {
1031   FindOurWindowAtPointInfo info;
1032   info.mInPointInScreen = aPointInScreen;
1033   info.mOutWnd = nullptr;
1034 
1035   // This will enumerate all top-level windows in order from top to bottom.
1036   EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
1037   return info.mOutWnd;
1038 }
1039 
1040 /* static */
GetInternalMessage(UINT aNativeMessage)1041 UINT WinUtils::GetInternalMessage(UINT aNativeMessage) {
1042   switch (aNativeMessage) {
1043     case WM_MOUSEWHEEL:
1044       return MOZ_WM_MOUSEVWHEEL;
1045     case WM_MOUSEHWHEEL:
1046       return MOZ_WM_MOUSEHWHEEL;
1047     case WM_VSCROLL:
1048       return MOZ_WM_VSCROLL;
1049     case WM_HSCROLL:
1050       return MOZ_WM_HSCROLL;
1051     default:
1052       return aNativeMessage;
1053   }
1054 }
1055 
1056 /* static */
GetNativeMessage(UINT aInternalMessage)1057 UINT WinUtils::GetNativeMessage(UINT aInternalMessage) {
1058   switch (aInternalMessage) {
1059     case MOZ_WM_MOUSEVWHEEL:
1060       return WM_MOUSEWHEEL;
1061     case MOZ_WM_MOUSEHWHEEL:
1062       return WM_MOUSEHWHEEL;
1063     case MOZ_WM_VSCROLL:
1064       return WM_VSCROLL;
1065     case MOZ_WM_HSCROLL:
1066       return WM_HSCROLL;
1067     default:
1068       return aInternalMessage;
1069   }
1070 }
1071 
1072 /* static */
GetMouseInputSource()1073 uint16_t WinUtils::GetMouseInputSource() {
1074   int32_t inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE;
1075   LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
1076   if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
1077     inputSource = (lParamExtraInfo & TABLET_INK_TOUCH)
1078                       ? dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
1079                       : dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
1080   }
1081   return static_cast<uint16_t>(inputSource);
1082 }
1083 
1084 /* static */
GetMousePointerID()1085 uint16_t WinUtils::GetMousePointerID() {
1086   LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
1087   return lParamExtraInfo & TABLET_INK_ID_MASK;
1088 }
1089 
1090 /* static */
GetIsMouseFromTouch(EventMessage aEventMessage)1091 bool WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage) {
1092   const uint32_t MOZ_T_I_SIGNATURE = TABLET_INK_TOUCH | TABLET_INK_SIGNATURE;
1093   const uint32_t MOZ_T_I_CHECK_TCH = TABLET_INK_TOUCH | TABLET_INK_CHECK;
1094   return ((aEventMessage == eMouseMove || aEventMessage == eMouseDown ||
1095            aEventMessage == eMouseUp || aEventMessage == eMouseAuxClick ||
1096            aEventMessage == eMouseDoubleClick) &&
1097           (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE) == MOZ_T_I_CHECK_TCH);
1098 }
1099 
1100 /* static */
InitMSG(UINT aMessage,WPARAM wParam,LPARAM lParam,HWND aWnd)1101 MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) {
1102   MSG msg;
1103   msg.message = aMessage;
1104   msg.wParam = wParam;
1105   msg.lParam = lParam;
1106   msg.hwnd = aWnd;
1107   return msg;
1108 }
1109 
EnumFirstChild(HWND hwnd,LPARAM lParam)1110 static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam) {
1111   *((HWND*)lParam) = hwnd;
1112   return FALSE;
1113 }
1114 
1115 /* static */
InvalidatePluginAsWorkaround(nsIWidget * aWidget,const LayoutDeviceIntRect & aRect)1116 void WinUtils::InvalidatePluginAsWorkaround(nsIWidget* aWidget,
1117                                             const LayoutDeviceIntRect& aRect) {
1118   aWidget->Invalidate(aRect);
1119 
1120   // XXX - Even more evil workaround!! See bug 762948, flash's bottom
1121   // level sandboxed window doesn't seem to get our invalidate. We send
1122   // an invalidate to it manually. This is totally specialized for this
1123   // bug, for other child window structures this will just be a more or
1124   // less bogus invalidate but since that should not have any bad
1125   // side-effects this will have to do for now.
1126   HWND current = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
1127 
1128   RECT windowRect;
1129   RECT parentRect;
1130 
1131   ::GetWindowRect(current, &parentRect);
1132 
1133   HWND next = current;
1134   do {
1135     current = next;
1136     ::EnumChildWindows(current, &EnumFirstChild, (LPARAM)&next);
1137     ::GetWindowRect(next, &windowRect);
1138     // This is relative to the screen, adjust it to be relative to the
1139     // window we're reconfiguring.
1140     windowRect.left -= parentRect.left;
1141     windowRect.top -= parentRect.top;
1142   } while (next != current && windowRect.top == 0 && windowRect.left == 0);
1143 
1144   if (windowRect.top == 0 && windowRect.left == 0) {
1145     RECT rect;
1146     rect.left = aRect.X();
1147     rect.top = aRect.Y();
1148     rect.right = aRect.XMost();
1149     rect.bottom = aRect.YMost();
1150 
1151     ::InvalidateRect(next, &rect, FALSE);
1152   }
1153 }
1154 
1155 #ifdef MOZ_PLACES
1156 /************************************************************************
1157  * Constructs as AsyncFaviconDataReady Object
1158  * @param aIOThread : the thread which performs the action
1159  * @param aURLShortcut : Differentiates between (false)Jumplistcache and
1160  *                       (true)Shortcutcache
1161  * @param aRunnable : Executed in the aIOThread when the favicon cache is
1162  *                    avaiable
1163  ************************************************************************/
1164 
AsyncFaviconDataReady(nsIURI * aNewURI,nsCOMPtr<nsIThread> & aIOThread,const bool aURLShortcut,already_AddRefed<nsIRunnable> aRunnable)1165 AsyncFaviconDataReady::AsyncFaviconDataReady(
1166     nsIURI* aNewURI, nsCOMPtr<nsIThread>& aIOThread, const bool aURLShortcut,
1167     already_AddRefed<nsIRunnable> aRunnable)
1168     : mNewURI(aNewURI),
1169       mIOThread(aIOThread),
1170       mRunnable(aRunnable),
1171       mURLShortcut(aURLShortcut) {}
1172 
1173 NS_IMETHODIMP
OnDownloadComplete(nsIDownloader * downloader,nsIRequest * request,nsISupports * ctxt,nsresult status,nsIFile * result)1174 myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
1175                                        nsIRequest* request, nsISupports* ctxt,
1176                                        nsresult status, nsIFile* result) {
1177   return NS_OK;
1178 }
1179 
OnFaviconDataNotAvailable(void)1180 nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
1181   if (!mURLShortcut) {
1182     return NS_OK;
1183   }
1184 
1185   nsCOMPtr<nsIFile> icoFile;
1186   nsresult rv =
1187       FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
1188   NS_ENSURE_SUCCESS(rv, rv);
1189 
1190   nsCOMPtr<nsIURI> mozIconURI;
1191   rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
1192   if (NS_FAILED(rv)) {
1193     return rv;
1194   }
1195 
1196   nsCOMPtr<nsIChannel> channel;
1197   rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI,
1198                      nsContentUtils::GetSystemPrincipal(),
1199                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
1200                      nsIContentPolicy::TYPE_INTERNAL_IMAGE);
1201 
1202   NS_ENSURE_SUCCESS(rv, rv);
1203 
1204   nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
1205   nsCOMPtr<nsIStreamListener> listener;
1206   rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
1207   NS_ENSURE_SUCCESS(rv, rv);
1208 
1209   return channel->AsyncOpen(listener);
1210 }
1211 
1212 NS_IMETHODIMP
OnComplete(nsIURI * aFaviconURI,uint32_t aDataLen,const uint8_t * aData,const nsACString & aMimeType,uint16_t aWidth)1213 AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
1214                                   const uint8_t* aData,
1215                                   const nsACString& aMimeType,
1216                                   uint16_t aWidth) {
1217   if (!aDataLen || !aData) {
1218     if (mURLShortcut) {
1219       OnFaviconDataNotAvailable();
1220     }
1221 
1222     return NS_OK;
1223   }
1224 
1225   nsCOMPtr<nsIFile> icoFile;
1226   nsresult rv =
1227       FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
1228   NS_ENSURE_SUCCESS(rv, rv);
1229 
1230   nsAutoString path;
1231   rv = icoFile->GetPath(path);
1232   NS_ENSURE_SUCCESS(rv, rv);
1233 
1234   // Decode the image from the format it was returned to us in (probably PNG)
1235   nsCOMPtr<imgIContainer> container;
1236   nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
1237   rv = imgtool->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData),
1238                                       aDataLen, aMimeType,
1239                                       getter_AddRefs(container));
1240   NS_ENSURE_SUCCESS(rv, rv);
1241 
1242   RefPtr<SourceSurface> surface = container->GetFrame(
1243       imgIContainer::FRAME_FIRST,
1244       imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
1245   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
1246 
1247   RefPtr<DataSourceSurface> dataSurface;
1248   IntSize size;
1249 
1250   if (mURLShortcut &&
1251       (surface->GetSize().width < 48 || surface->GetSize().height < 48)) {
1252     // Create a 48x48 surface and paint the icon into the central rect.
1253     size.width = std::max(surface->GetSize().width, 48);
1254     size.height = std::max(surface->GetSize().height, 48);
1255     dataSurface =
1256         Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1257     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
1258 
1259     DataSourceSurface::MappedSurface map;
1260     if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
1261       return NS_ERROR_FAILURE;
1262     }
1263 
1264     RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1265         BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
1266         dataSurface->GetFormat());
1267     if (!dt) {
1268       gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
1269                       "CreateDrawTargetForData";
1270       return NS_ERROR_OUT_OF_MEMORY;
1271     }
1272     dt->FillRect(Rect(0, 0, size.width, size.height),
1273                  ColorPattern(ToDeviceColor(sRGBColor::OpaqueWhite())));
1274     IntPoint point;
1275     point.x = (size.width - surface->GetSize().width) / 2;
1276     point.y = (size.height - surface->GetSize().height) / 2;
1277     dt->DrawSurface(surface,
1278                     Rect(point.x, point.y, surface->GetSize().width,
1279                          surface->GetSize().height),
1280                     Rect(Point(0, 0), Size(surface->GetSize().width,
1281                                            surface->GetSize().height)));
1282 
1283     dataSurface->Unmap();
1284   } else {
1285     // By using the input image surface's size, we may end up encoding
1286     // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
1287     // Windows will resize appropriately for us. If we want to encode ourselves
1288     // one day because we like our resizing better, we'd have to manually
1289     // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
1290     // SM_CYSMICON. We don't support resizing images asynchronously at the
1291     // moment anyway so getting the DPI aware icon size won't help.
1292     size.width = surface->GetSize().width;
1293     size.height = surface->GetSize().height;
1294     dataSurface = surface->GetDataSurface();
1295     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
1296   }
1297 
1298   // Allocate a new buffer that we own and can use out of line in
1299   // another thread.
1300   UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface);
1301   if (!data) {
1302     return NS_ERROR_OUT_OF_MEMORY;
1303   }
1304   int32_t stride = 4 * size.width;
1305 
1306   // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
1307   nsCOMPtr<nsIRunnable> event =
1308       new AsyncEncodeAndWriteIcon(path, std::move(data), stride, size.width,
1309                                   size.height, mRunnable.forget());
1310   mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
1311 
1312   return NS_OK;
1313 }
1314 #endif
1315 
1316 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
1317 // in
AsyncEncodeAndWriteIcon(const nsAString & aIconPath,UniquePtr<uint8_t[]> aBuffer,uint32_t aStride,uint32_t aWidth,uint32_t aHeight,already_AddRefed<nsIRunnable> aRunnable)1318 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
1319     const nsAString& aIconPath, UniquePtr<uint8_t[]> aBuffer, uint32_t aStride,
1320     uint32_t aWidth, uint32_t aHeight, already_AddRefed<nsIRunnable> aRunnable)
1321     : mIconPath(aIconPath),
1322       mBuffer(std::move(aBuffer)),
1323       mRunnable(aRunnable),
1324       mStride(aStride),
1325       mWidth(aWidth),
1326       mHeight(aHeight) {}
1327 
Run()1328 NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
1329   MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");
1330 
1331   // Note that since we're off the main thread we can't use
1332   // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
1333   RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
1334       mBuffer.get(), mStride, IntSize(mWidth, mHeight),
1335       SurfaceFormat::B8G8R8A8);
1336 
1337   FILE* file = _wfopen(mIconPath.get(), L"wb");
1338   if (!file) {
1339     // Maybe the directory doesn't exist; try creating it, then fopen again.
1340     nsresult rv = NS_ERROR_FAILURE;
1341     nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
1342     if (comFile) {
1343       rv = comFile->InitWithPath(mIconPath);
1344       if (NS_SUCCEEDED(rv)) {
1345         nsCOMPtr<nsIFile> dirPath;
1346         comFile->GetParent(getter_AddRefs(dirPath));
1347         if (dirPath) {
1348           rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
1349           if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
1350             file = _wfopen(mIconPath.get(), L"wb");
1351             if (!file) {
1352               rv = NS_ERROR_FAILURE;
1353             }
1354           }
1355         }
1356       }
1357     }
1358     if (!file) {
1359       return rv;
1360     }
1361   }
1362   nsresult rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
1363                                               gfxUtils::eBinaryEncode, file);
1364   fclose(file);
1365   NS_ENSURE_SUCCESS(rv, rv);
1366 
1367   if (mRunnable) {
1368     mRunnable->Run();
1369   }
1370   return rv;
1371 }
1372 
~AsyncEncodeAndWriteIcon()1373 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {}
1374 
AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent)1375 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
1376     bool aIgnoreRecent)
1377     : mIgnoreRecent(aIgnoreRecent) {
1378   // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
1379   // threads, as it reads a pref, so cache its value here.
1380   mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600;
1381 
1382   // Prepare the profile directory cache on the main thread, to ensure we wont
1383   // do this on non-main threads.
1384   Unused << NS_GetSpecialDirectory("ProfLDS",
1385                                    getter_AddRefs(mJumpListCacheDir));
1386 }
1387 
Run()1388 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() {
1389   if (!mJumpListCacheDir) {
1390     return NS_ERROR_FAILURE;
1391   }
1392   // Construct the path of our jump list cache
1393   nsresult rv = mJumpListCacheDir->AppendNative(
1394       nsDependentCString(FaviconHelper::kJumpListCacheDir));
1395   NS_ENSURE_SUCCESS(rv, rv);
1396 
1397   nsCOMPtr<nsIDirectoryEnumerator> entries;
1398   rv = mJumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
1399   NS_ENSURE_SUCCESS(rv, rv);
1400 
1401   // Loop through each directory entry and remove all ICO files found
1402   do {
1403     nsCOMPtr<nsIFile> currFile;
1404     if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
1405       break;
1406 
1407     nsAutoString path;
1408     if (NS_FAILED(currFile->GetPath(path))) continue;
1409 
1410     if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
1411       // Check if the cached ICO file exists
1412       bool exists;
1413       if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
1414 
1415       if (mIgnoreRecent) {
1416         // Check to make sure the icon wasn't just recently created.
1417         // If it was created recently, don't delete it yet.
1418         int64_t fileModTime = 0;
1419         rv = currFile->GetLastModifiedTime(&fileModTime);
1420         fileModTime /= PR_MSEC_PER_SEC;
1421         // If the icon is older than the regeneration time (+ 10 min to be
1422         // safe), then it's old and we can get rid of it.
1423         // This code is only hit directly after a regeneration.
1424         int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1425         if (NS_FAILED(rv) || (nowTime - fileModTime) < mIcoNoDeleteSeconds) {
1426           continue;
1427         }
1428       }
1429 
1430       // We found an ICO file that exists, so we should remove it
1431       currFile->Remove(false);
1432     }
1433   } while (true);
1434 
1435   return NS_OK;
1436 }
1437 
~AsyncDeleteAllFaviconsFromDisk()1438 AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() {}
1439 
1440 /*
1441  * (static) If the data is available, will return the path on disk where
1442  * the favicon for page aFaviconPageURI is stored.  If the favicon does not
1443  * exist, or its cache is expired, this method will kick off an async request
1444  * for the icon so that next time the method is called it will be available.
1445  * @param aFaviconPageURI The URI of the page to obtain
1446  * @param aICOFilePath The path of the icon file
1447  * @param aIOThread The thread to perform the Fetch on
1448  * @param aURLShortcut to distinguish between jumplistcache(false) and
1449  *                     shortcutcache(true)
1450  * @param aRunnable Executed in the aIOThread when the favicon cache is
1451  *                  avaiable
1452  */
ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,nsString & aICOFilePath,nsCOMPtr<nsIThread> & aIOThread,bool aURLShortcut,already_AddRefed<nsIRunnable> aRunnable)1453 nsresult FaviconHelper::ObtainCachedIconFile(
1454     nsCOMPtr<nsIURI> aFaviconPageURI, nsString& aICOFilePath,
1455     nsCOMPtr<nsIThread>& aIOThread, bool aURLShortcut,
1456     already_AddRefed<nsIRunnable> aRunnable) {
1457   nsCOMPtr<nsIRunnable> runnable = aRunnable;
1458   // Obtain the ICO file path
1459   nsCOMPtr<nsIFile> icoFile;
1460   nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
1461   NS_ENSURE_SUCCESS(rv, rv);
1462 
1463   // Check if the cached ICO file already exists
1464   bool exists;
1465   rv = icoFile->Exists(&exists);
1466   NS_ENSURE_SUCCESS(rv, rv);
1467 
1468   if (exists) {
1469     // Obtain the file's last modification date in seconds
1470     int64_t fileModTime = 0;
1471     rv = icoFile->GetLastModifiedTime(&fileModTime);
1472     fileModTime /= PR_MSEC_PER_SEC;
1473     int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
1474     int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1475 
1476     // If the last mod call failed or the icon is old then re-cache it
1477     // This check is in case the favicon of a page changes
1478     // the next time we try to build the jump list, the data will be available.
1479     if (NS_FAILED(rv) || (nowTime - fileModTime) > icoReCacheSecondsTimeout) {
1480       CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1481                                        aURLShortcut, runnable.forget());
1482       return NS_ERROR_NOT_AVAILABLE;
1483     }
1484   } else {
1485     // The file does not exist yet, obtain it async from the favicon service so
1486     // that the next time we try to build the jump list it'll be available.
1487     CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
1488                                      aURLShortcut, runnable.forget());
1489     return NS_ERROR_NOT_AVAILABLE;
1490   }
1491 
1492   // The icoFile is filled with a path that exists, get its path
1493   rv = icoFile->GetPath(aICOFilePath);
1494   return rv;
1495 }
1496 
HashURI(nsCOMPtr<nsICryptoHash> & aCryptoHash,nsIURI * aUri,nsACString & aUriHash)1497 nsresult FaviconHelper::HashURI(nsCOMPtr<nsICryptoHash>& aCryptoHash,
1498                                 nsIURI* aUri, nsACString& aUriHash) {
1499   if (!aUri) return NS_ERROR_INVALID_ARG;
1500 
1501   nsAutoCString spec;
1502   nsresult rv = aUri->GetSpec(spec);
1503   NS_ENSURE_SUCCESS(rv, rv);
1504 
1505   if (!aCryptoHash) {
1506     aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1507     NS_ENSURE_SUCCESS(rv, rv);
1508   }
1509 
1510   rv = aCryptoHash->Init(nsICryptoHash::MD5);
1511   NS_ENSURE_SUCCESS(rv, rv);
1512   rv = aCryptoHash->Update(
1513       reinterpret_cast<const uint8_t*>(spec.BeginReading()), spec.Length());
1514   NS_ENSURE_SUCCESS(rv, rv);
1515   rv = aCryptoHash->Finish(true, aUriHash);
1516   NS_ENSURE_SUCCESS(rv, rv);
1517 
1518   return NS_OK;
1519 }
1520 
1521 // (static) Obtains the ICO file for the favicon at page aFaviconPageURI
1522 // If successful, the file path on disk is in the format:
1523 // <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,nsCOMPtr<nsIFile> & aICOFile,bool aURLShortcut)1524 nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
1525                                           nsCOMPtr<nsIFile>& aICOFile,
1526                                           bool aURLShortcut) {
1527   // Hash the input URI and replace any / with _
1528   nsAutoCString inputURIHash;
1529   nsCOMPtr<nsICryptoHash> cryptoHash;
1530   nsresult rv = HashURI(cryptoHash, aFaviconPageURI, inputURIHash);
1531   NS_ENSURE_SUCCESS(rv, rv);
1532   char* cur = inputURIHash.BeginWriting();
1533   char* end = inputURIHash.EndWriting();
1534   for (; cur < end; ++cur) {
1535     if ('/' == *cur) {
1536       *cur = '_';
1537     }
1538   }
1539 
1540   // Obtain the local profile directory and construct the output icon file path
1541   rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
1542   NS_ENSURE_SUCCESS(rv, rv);
1543   if (!aURLShortcut)
1544     rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
1545   else
1546     rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
1547   NS_ENSURE_SUCCESS(rv, rv);
1548 
1549   // Append the icon extension
1550   inputURIHash.AppendLiteral(".ico");
1551   rv = aICOFile->AppendNative(inputURIHash);
1552 
1553   return rv;
1554 }
1555 
1556 // (static) Asynchronously creates a cached ICO file on disk for the favicon of
1557 // page aFaviconPageURI and stores it to disk at the path of aICOFile.
CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI,nsCOMPtr<nsIFile> aICOFile,nsCOMPtr<nsIThread> & aIOThread,bool aURLShortcut,already_AddRefed<nsIRunnable> aRunnable)1558 nsresult FaviconHelper::CacheIconFileFromFaviconURIAsync(
1559     nsCOMPtr<nsIURI> aFaviconPageURI, nsCOMPtr<nsIFile> aICOFile,
1560     nsCOMPtr<nsIThread>& aIOThread, bool aURLShortcut,
1561     already_AddRefed<nsIRunnable> aRunnable) {
1562   nsCOMPtr<nsIRunnable> runnable = aRunnable;
1563 #ifdef MOZ_PLACES
1564   // Obtain the favicon service and get the favicon for the specified page
1565   nsCOMPtr<nsIFaviconService> favIconSvc(
1566       do_GetService("@mozilla.org/browser/favicon-service;1"));
1567   NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
1568 
1569   nsCOMPtr<nsIFaviconDataCallback> callback =
1570       new mozilla::widget::AsyncFaviconDataReady(
1571           aFaviconPageURI, aIOThread, aURLShortcut, runnable.forget());
1572 
1573   favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
1574 #endif
1575   return NS_OK;
1576 }
1577 
1578 // Obtains the jump list 'ICO cache timeout in seconds' pref
GetICOCacheSecondsTimeout()1579 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
1580   // Only obtain the setting at most once from the pref service.
1581   // In the rare case that 2 threads call this at the same
1582   // time it is no harm and we will simply obtain the pref twice.
1583   // None of the taskbar list prefs are currently updated via a
1584   // pref observer so I think this should suffice.
1585   const int32_t kSecondsPerDay = 86400;
1586   static bool alreadyObtained = false;
1587   static int32_t icoReCacheSecondsTimeout = kSecondsPerDay;
1588   if (alreadyObtained) {
1589     return icoReCacheSecondsTimeout;
1590   }
1591 
1592   // Obtain the pref
1593   const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1594   icoReCacheSecondsTimeout =
1595       Preferences::GetInt(PREF_ICOTIMEOUT, kSecondsPerDay);
1596   alreadyObtained = true;
1597   return icoReCacheSecondsTimeout;
1598 }
1599 
1600 /* static */
GetShellItemPath(IShellItem * aItem,nsString & aResultString)1601 bool WinUtils::GetShellItemPath(IShellItem* aItem, nsString& aResultString) {
1602   NS_ENSURE_TRUE(aItem, false);
1603   LPWSTR str = nullptr;
1604   if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) return false;
1605   aResultString.Assign(str);
1606   CoTaskMemFree(str);
1607   return !aResultString.IsEmpty();
1608 }
1609 
1610 /* static */
ConvertHRGNToRegion(HRGN aRgn)1611 LayoutDeviceIntRegion WinUtils::ConvertHRGNToRegion(HRGN aRgn) {
1612   NS_ASSERTION(aRgn, "Don't pass NULL region here");
1613 
1614   LayoutDeviceIntRegion rgn;
1615 
1616   DWORD size = ::GetRegionData(aRgn, 0, nullptr);
1617   AutoTArray<uint8_t, 100> buffer;
1618   buffer.SetLength(size);
1619 
1620   RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements());
1621   if (!::GetRegionData(aRgn, size, data)) return rgn;
1622 
1623   if (data->rdh.nCount > MAX_RECTS_IN_REGION) {
1624     rgn = ToIntRect(data->rdh.rcBound);
1625     return rgn;
1626   }
1627 
1628   RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
1629   for (uint32_t i = 0; i < data->rdh.nCount; ++i) {
1630     RECT* r = rects + i;
1631     rgn.Or(rgn, ToIntRect(*r));
1632   }
1633 
1634   return rgn;
1635 }
1636 
ToIntRect(const RECT & aRect)1637 LayoutDeviceIntRect WinUtils::ToIntRect(const RECT& aRect) {
1638   return LayoutDeviceIntRect(aRect.left, aRect.top, aRect.right - aRect.left,
1639                              aRect.bottom - aRect.top);
1640 }
1641 
1642 /* static */
IsIMEEnabled(const InputContext & aInputContext)1643 bool WinUtils::IsIMEEnabled(const InputContext& aInputContext) {
1644   return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
1645 }
1646 
1647 /* static */
IsIMEEnabled(IMEEnabled aIMEState)1648 bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState) {
1649   return aIMEState == IMEEnabled::Enabled;
1650 }
1651 
1652 /* static */
SetupKeyModifiersSequence(nsTArray<KeyPair> * aArray,uint32_t aModifiers,UINT aMessage)1653 void WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray,
1654                                          uint32_t aModifiers, UINT aMessage) {
1655   MOZ_ASSERT(!(aModifiers & nsIWidget::ALTGRAPH) ||
1656              !(aModifiers & (nsIWidget::CTRL_L | nsIWidget::ALT_R)));
1657   if (aMessage == WM_KEYUP) {
1658     // If AltGr is released, ControlLeft key is released first, then,
1659     // AltRight key is released.
1660     if (aModifiers & nsIWidget::ALTGRAPH) {
1661       aArray->AppendElement(
1662           KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1663       aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1664     }
1665     for (uint32_t i = ArrayLength(sModifierKeyMap); i; --i) {
1666       const uint32_t* map = sModifierKeyMap[i - 1];
1667       if (aModifiers & map[0]) {
1668         aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1669       }
1670     }
1671   } else {
1672     for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) {
1673       const uint32_t* map = sModifierKeyMap[i];
1674       if (aModifiers & map[0]) {
1675         aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
1676       }
1677     }
1678     // If AltGr is pressed, ControlLeft key is pressed first, then,
1679     // AltRight key is pressed.
1680     if (aModifiers & nsIWidget::ALTGRAPH) {
1681       aArray->AppendElement(
1682           KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
1683       aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
1684     }
1685   }
1686 }
1687 
1688 /* static */
WriteBitmap(nsIFile * aFile,imgIContainer * aImage)1689 nsresult WinUtils::WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
1690   RefPtr<SourceSurface> surface = aImage->GetFrame(
1691       imgIContainer::FRAME_FIRST,
1692       imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
1693   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
1694 
1695   return WriteBitmap(aFile, surface);
1696 }
1697 
1698 /* static */
WriteBitmap(nsIFile * aFile,SourceSurface * surface)1699 nsresult WinUtils::WriteBitmap(nsIFile* aFile, SourceSurface* surface) {
1700   nsresult rv;
1701 
1702   // For either of the following formats we want to set the biBitCount member
1703   // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
1704   // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
1705   // for the BI_RGB value we use for the biCompression member.
1706   MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
1707              surface->GetFormat() == SurfaceFormat::B8G8R8X8);
1708 
1709   RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
1710   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
1711 
1712   int32_t width = dataSurface->GetSize().width;
1713   int32_t height = dataSurface->GetSize().height;
1714   int32_t bytesPerPixel = 4 * sizeof(uint8_t);
1715   uint32_t bytesPerRow = bytesPerPixel * width;
1716   bool hasAlpha = surface->GetFormat() == SurfaceFormat::B8G8R8A8;
1717 
1718   // initialize these bitmap structs which we will later
1719   // serialize directly to the head of the bitmap file
1720   BITMAPV4HEADER bmi;
1721   memset(&bmi, 0, sizeof(BITMAPV4HEADER));
1722   bmi.bV4Size = sizeof(BITMAPV4HEADER);
1723   bmi.bV4Width = width;
1724   bmi.bV4Height = height;
1725   bmi.bV4Planes = 1;
1726   bmi.bV4BitCount = (WORD)bytesPerPixel * 8;
1727   bmi.bV4V4Compression = hasAlpha ? BI_BITFIELDS : BI_RGB;
1728   bmi.bV4SizeImage = bytesPerRow * height;
1729   bmi.bV4CSType = LCS_sRGB;
1730   if (hasAlpha) {
1731     bmi.bV4RedMask = 0x00FF0000;
1732     bmi.bV4GreenMask = 0x0000FF00;
1733     bmi.bV4BlueMask = 0x000000FF;
1734     bmi.bV4AlphaMask = 0xFF000000;
1735   }
1736 
1737   BITMAPFILEHEADER bf;
1738   DWORD colormask[3];
1739   bf.bfType = 0x4D42;  // 'BM'
1740   bf.bfReserved1 = 0;
1741   bf.bfReserved2 = 0;
1742   bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) +
1743                  (hasAlpha ? sizeof(colormask) : 0);
1744   bf.bfSize = bf.bfOffBits + bmi.bV4SizeImage;
1745 
1746   // get a file output stream
1747   nsCOMPtr<nsIOutputStream> stream;
1748   rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
1749   NS_ENSURE_SUCCESS(rv, rv);
1750 
1751   DataSourceSurface::MappedSurface map;
1752   if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
1753     return NS_ERROR_FAILURE;
1754   }
1755 
1756   // write the bitmap headers and rgb pixel data to the file
1757   rv = NS_ERROR_FAILURE;
1758   if (stream) {
1759     uint32_t written;
1760     stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
1761     if (written == sizeof(BITMAPFILEHEADER)) {
1762       stream->Write((const char*)&bmi, sizeof(BITMAPV4HEADER), &written);
1763       if (written == sizeof(BITMAPV4HEADER)) {
1764         if (hasAlpha) {
1765           // color mask
1766           colormask[0] = 0x00FF0000;
1767           colormask[1] = 0x0000FF00;
1768           colormask[2] = 0x000000FF;
1769 
1770           stream->Write((const char*)colormask, sizeof(colormask), &written);
1771         }
1772         if (!hasAlpha || written == sizeof(colormask)) {
1773           // write out the image data backwards because the desktop won't
1774           // show bitmaps with negative heights for top-to-bottom
1775           uint32_t i = map.mStride * height;
1776           do {
1777             i -= map.mStride;
1778             stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
1779             if (written == bytesPerRow) {
1780               rv = NS_OK;
1781             } else {
1782               rv = NS_ERROR_FAILURE;
1783               break;
1784             }
1785           } while (i != 0);
1786         }
1787       }
1788     }
1789 
1790     stream->Close();
1791   }
1792 
1793   dataSurface->Unmap();
1794 
1795   return rv;
1796 }
1797 
1798 // This is in use here and in dom/events/TouchEvent.cpp
1799 /* static */
IsTouchDeviceSupportPresent()1800 uint32_t WinUtils::IsTouchDeviceSupportPresent() {
1801   int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
1802   return (touchCapabilities & NID_READY) &&
1803          (touchCapabilities & (NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH));
1804 }
1805 
1806 /* static */
GetMaxTouchPoints()1807 uint32_t WinUtils::GetMaxTouchPoints() {
1808   if (IsTouchDeviceSupportPresent()) {
1809     return GetSystemMetrics(SM_MAXIMUMTOUCHES);
1810   }
1811   return 0;
1812 }
1813 
1814 /* static */
1815 POWER_PLATFORM_ROLE
GetPowerPlatformRole()1816 WinUtils::GetPowerPlatformRole() {
1817   typedef POWER_PLATFORM_ROLE(WINAPI *
1818                               PowerDeterminePlatformRoleEx)(ULONG Version);
1819   static PowerDeterminePlatformRoleEx power_determine_platform_role =
1820       reinterpret_cast<PowerDeterminePlatformRoleEx>(::GetProcAddress(
1821           ::LoadLibraryW(L"PowrProf.dll"), "PowerDeterminePlatformRoleEx"));
1822 
1823   POWER_PLATFORM_ROLE powerPlatformRole = PlatformRoleUnspecified;
1824   if (!power_determine_platform_role) {
1825     return powerPlatformRole;
1826   }
1827 
1828   return power_determine_platform_role(POWER_PLATFORM_ROLE_V2);
1829 }
1830 
IsWindows10TabletMode()1831 static bool IsWindows10TabletMode() {
1832   nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1833       do_GetService("@mozilla.org/windows-ui-utils;1"));
1834   if (NS_WARN_IF(!uiUtils)) {
1835     return false;
1836   }
1837   bool isInTabletMode = false;
1838   uiUtils->GetInTabletMode(&isInTabletMode);
1839   return isInTabletMode;
1840 }
1841 
CallGetAutoRotationState(AR_STATE * aRotationState)1842 static bool CallGetAutoRotationState(AR_STATE* aRotationState) {
1843   typedef BOOL(WINAPI * GetAutoRotationStateFunc)(PAR_STATE pState);
1844   static GetAutoRotationStateFunc get_auto_rotation_state_func =
1845       reinterpret_cast<GetAutoRotationStateFunc>(::GetProcAddress(
1846           GetModuleHandleW(L"user32.dll"), "GetAutoRotationState"));
1847   if (get_auto_rotation_state_func) {
1848     ZeroMemory(aRotationState, sizeof(AR_STATE));
1849     return get_auto_rotation_state_func(aRotationState);
1850   }
1851   return false;
1852 }
1853 
IsTabletDevice()1854 static bool IsTabletDevice() {
1855   // Guarantees that:
1856   // - The device has a touch screen.
1857   // - It is used as a tablet which means that it has no keyboard connected.
1858   // On Windows 10 it means that it is verifying with ConvertibleSlateMode.
1859 
1860   if (!IsWin8OrLater()) {
1861     return false;
1862   }
1863 
1864   if (IsWindows10TabletMode()) {
1865     return true;
1866   }
1867 
1868   if (!GetSystemMetrics(SM_MAXIMUMTOUCHES)) {
1869     return false;
1870   }
1871 
1872   // If the device is docked, the user is treating the device as a PC.
1873   if (GetSystemMetrics(SM_SYSTEMDOCKED)) {
1874     return false;
1875   }
1876 
1877   // If the device is not supporting rotation, it's unlikely to be a tablet,
1878   // a convertible or a detachable. See:
1879   // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
1880   AR_STATE rotation_state;
1881   if (CallGetAutoRotationState(&rotation_state) &&
1882       (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR))) {
1883     return false;
1884   }
1885 
1886   // PlatformRoleSlate was added in Windows 8+.
1887   POWER_PLATFORM_ROLE role = WinUtils::GetPowerPlatformRole();
1888   if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
1889     return !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
1890   }
1891   return false;
1892 }
1893 
IsMousePresent()1894 static bool IsMousePresent() {
1895   if (!::GetSystemMetrics(SM_MOUSEPRESENT)) {
1896     return false;
1897   }
1898 
1899   DWORD count = InputDeviceUtils::CountMouseDevices();
1900   if (!count) {
1901     return false;
1902   }
1903 
1904   // If there is a mouse device and if this machine is a tablet or has a
1905   // digitizer, that's counted as the mouse device.
1906   // FIXME: Bug 1495938:  We should drop this heuristic way once we find out a
1907   // reliable way to tell there is no mouse or not.
1908   if (count == 1 &&
1909       (WinUtils::IsTouchDeviceSupportPresent() || IsTabletDevice())) {
1910     return false;
1911   }
1912 
1913   return true;
1914 }
1915 
1916 /* static */
GetPrimaryPointerCapabilities()1917 PointerCapabilities WinUtils::GetPrimaryPointerCapabilities() {
1918   if (IsTabletDevice()) {
1919     return PointerCapabilities::Coarse;
1920   }
1921 
1922   if (IsMousePresent()) {
1923     return PointerCapabilities::Fine | PointerCapabilities::Hover;
1924   }
1925 
1926   if (IsTouchDeviceSupportPresent()) {
1927     return PointerCapabilities::Coarse;
1928   }
1929 
1930   return PointerCapabilities::None;
1931 }
1932 
1933 /* static */
GetAllPointerCapabilities()1934 PointerCapabilities WinUtils::GetAllPointerCapabilities() {
1935   PointerCapabilities result = PointerCapabilities::None;
1936 
1937   if (IsTabletDevice() || IsTouchDeviceSupportPresent()) {
1938     result |= PointerCapabilities::Coarse;
1939   }
1940 
1941   if (IsMousePresent()) {
1942     result |= PointerCapabilities::Fine | PointerCapabilities::Hover;
1943   }
1944 
1945   return result;
1946 }
1947 
1948 /* static */
ResolveJunctionPointsAndSymLinks(std::wstring & aPath)1949 bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath) {
1950   LOG_D("ResolveJunctionPointsAndSymLinks: Resolving path: %S", aPath.c_str());
1951 
1952   wchar_t path[MAX_PATH] = {0};
1953 
1954   nsAutoHandle handle(::CreateFileW(
1955       aPath.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1956       nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
1957 
1958   if (handle == INVALID_HANDLE_VALUE) {
1959     LOG_E("Failed to open file handle to resolve path. GetLastError=%d",
1960           GetLastError());
1961     return false;
1962   }
1963 
1964   DWORD pathLen = GetFinalPathNameByHandleW(
1965       handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1966   if (pathLen == 0 || pathLen >= MAX_PATH) {
1967     LOG_E("GetFinalPathNameByHandleW failed. GetLastError=%d", GetLastError());
1968     return false;
1969   }
1970   aPath = path;
1971 
1972   // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
1973   // but that confuses some APIs so strip it off. It will also put
1974   // '\\?\UNC\' in front of network paths, we convert that to '\\'.
1975   if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
1976     aPath.erase(2, 6);
1977   } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
1978     aPath.erase(0, 4);
1979   }
1980 
1981   LOG_D("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
1982         aPath.c_str());
1983   return true;
1984 }
1985 
1986 /* static */
ResolveJunctionPointsAndSymLinks(nsIFile * aPath)1987 bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile* aPath) {
1988   MOZ_ASSERT(aPath);
1989 
1990   nsAutoString filePath;
1991   nsresult rv = aPath->GetPath(filePath);
1992   if (NS_WARN_IF(NS_FAILED(rv))) {
1993     return false;
1994   }
1995 
1996   std::wstring resolvedPath(filePath.get());
1997   if (!ResolveJunctionPointsAndSymLinks(resolvedPath)) {
1998     return false;
1999   }
2000 
2001   rv = aPath->InitWithPath(nsDependentString(resolvedPath.c_str()));
2002   if (NS_WARN_IF(NS_FAILED(rv))) {
2003     return false;
2004   }
2005 
2006   return true;
2007 }
2008 
2009 /* static */
RunningFromANetworkDrive()2010 bool WinUtils::RunningFromANetworkDrive() {
2011   wchar_t exePath[MAX_PATH];
2012   if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) {
2013     return false;
2014   }
2015 
2016   std::wstring exeString(exePath);
2017   if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString)) {
2018     return false;
2019   }
2020 
2021   wchar_t volPath[MAX_PATH];
2022   if (!::GetVolumePathNameW(exeString.c_str(), volPath, MAX_PATH)) {
2023     return false;
2024   }
2025 
2026   return (::GetDriveTypeW(volPath) == DRIVE_REMOTE);
2027 }
2028 
2029 /* static */
GetModuleFullPath(HMODULE aModuleHandle,nsAString & aPath)2030 bool WinUtils::GetModuleFullPath(HMODULE aModuleHandle, nsAString& aPath) {
2031   size_t bufferSize = MAX_PATH;
2032   size_t len = 0;
2033   while (true) {
2034     aPath.SetLength(bufferSize);
2035     len = (size_t)::GetModuleFileNameW(
2036         aModuleHandle, (char16ptr_t)aPath.BeginWriting(), bufferSize);
2037     if (!len) {
2038       return false;
2039     }
2040     if (len == bufferSize && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
2041       bufferSize *= 2;
2042       continue;
2043     }
2044     aPath.Truncate(len);
2045     break;
2046   }
2047   return true;
2048 }
2049 
2050 /* static */
CanonicalizePath(nsAString & aPath)2051 bool WinUtils::CanonicalizePath(nsAString& aPath) {
2052   wchar_t tempPath[MAX_PATH + 1];
2053   if (!PathCanonicalizeW(tempPath,
2054                          (char16ptr_t)PromiseFlatString(aPath).get())) {
2055     return false;
2056   }
2057   aPath = tempPath;
2058   MOZ_ASSERT(aPath.Length() <= MAX_PATH);
2059   return true;
2060 }
2061 
2062 /* static */
MakeLongPath(nsAString & aPath)2063 bool WinUtils::MakeLongPath(nsAString& aPath) {
2064   wchar_t tempPath[MAX_PATH + 1];
2065   DWORD longResult =
2066       GetLongPathNameW((char16ptr_t)PromiseFlatString(aPath).get(), tempPath,
2067                        ArrayLength(tempPath));
2068   if (longResult > ArrayLength(tempPath)) {
2069     // Our buffer is too short, and we're guaranteeing <= MAX_PATH results.
2070     return false;
2071   } else if (longResult) {
2072     // Success.
2073     aPath = tempPath;
2074     MOZ_ASSERT(aPath.Length() <= MAX_PATH);
2075   }
2076   // GetLongPathNameW returns 0 if the path is not found or is not rooted,
2077   // but we shouldn't consider that a failure condition.
2078   return true;
2079 }
2080 
2081 /* static */
UnexpandEnvVars(nsAString & aPath)2082 bool WinUtils::UnexpandEnvVars(nsAString& aPath) {
2083   wchar_t tempPath[MAX_PATH + 1];
2084   // PathUnExpandEnvStringsW returns false if it doesn't make any
2085   // substitutions. Silently continue using the unaltered path.
2086   if (PathUnExpandEnvStringsW((char16ptr_t)PromiseFlatString(aPath).get(),
2087                               tempPath, ArrayLength(tempPath))) {
2088     aPath = tempPath;
2089     MOZ_ASSERT(aPath.Length() <= MAX_PATH);
2090   }
2091   return true;
2092 }
2093 
2094 /* static */
BuildWhitelist()2095 WinUtils::WhitelistVec WinUtils::BuildWhitelist() {
2096   WhitelistVec result;
2097 
2098   Unused << result.emplaceBack(
2099       std::make_pair(nsString(u"%ProgramFiles%"_ns), nsDependentString()));
2100 
2101   // When no substitution is required, set the void flag
2102   result.back().second.SetIsVoid(true);
2103 
2104   Unused << result.emplaceBack(
2105       std::make_pair(nsString(u"%SystemRoot%"_ns), nsDependentString()));
2106   result.back().second.SetIsVoid(true);
2107 
2108   wchar_t tmpPath[MAX_PATH + 1] = {};
2109   if (GetTempPath(MAX_PATH, tmpPath)) {
2110     // GetTempPath's result always ends with a backslash, which we don't want
2111     uint32_t tmpPathLen = wcslen(tmpPath);
2112     if (tmpPathLen) {
2113       tmpPath[tmpPathLen - 1] = 0;
2114     }
2115 
2116     nsAutoString cleanTmpPath(tmpPath);
2117     if (UnexpandEnvVars(cleanTmpPath)) {
2118       constexpr auto tempVar = u"%TEMP%"_ns;
2119       Unused << result.emplaceBack(std::make_pair(
2120           nsString(cleanTmpPath), nsDependentString(tempVar, 0)));
2121     }
2122   }
2123 
2124   // If we add more items to the whitelist, ensure we still don't invoke an
2125   // unnecessary heap allocation.
2126   MOZ_ASSERT(result.length() <= kMaxWhitelistedItems);
2127 
2128   return result;
2129 }
2130 
2131 /**
2132  * This function provides an array of (system path, substitution) pairs that are
2133  * considered to be acceptable with respect to privacy, for the purposes of
2134  * submitting within telemetry or crash reports.
2135  *
2136  * The substitution string's void flag may be set. If it is, no subsitution is
2137  * necessary. Otherwise, the consumer should replace the system path with the
2138  * substitution.
2139  *
2140  * @see PreparePathForTelemetry for an example of its usage.
2141  */
2142 /* static */
GetWhitelistedPaths()2143 const WinUtils::WhitelistVec& WinUtils::GetWhitelistedPaths() {
2144   static WhitelistVec sWhitelist([]() -> WhitelistVec {
2145     auto setClearFn = [ptr = &sWhitelist]() -> void {
2146       RunOnShutdown([ptr]() -> void { ptr->clear(); },
2147                     ShutdownPhase::XPCOMShutdownFinal);
2148     };
2149 
2150     if (NS_IsMainThread()) {
2151       setClearFn();
2152     } else {
2153       SchedulerGroup::Dispatch(
2154           TaskCategory::Other,
2155           NS_NewRunnableFunction("WinUtils::GetWhitelistedPaths",
2156                                  std::move(setClearFn)));
2157     }
2158 
2159     return BuildWhitelist();
2160   }());
2161   return sWhitelist;
2162 }
2163 
2164 /**
2165  * This function is located here (as opposed to nsSystemInfo or elsewhere)
2166  * because we need to gather this information as early as possible during
2167  * startup.
2168  */
2169 /* static */
GetAppInitDLLs(nsAString & aOutput)2170 bool WinUtils::GetAppInitDLLs(nsAString& aOutput) {
2171   aOutput.Truncate();
2172   HKEY hkey = NULL;
2173   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2174                     L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
2175                     0, KEY_QUERY_VALUE, &hkey)) {
2176     return false;
2177   }
2178   nsAutoRegKey key(hkey);
2179   LONG status;
2180   const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
2181   DWORD loadAppInitDLLs = 0;
2182   DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
2183   status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr, nullptr,
2184                             (LPBYTE)&loadAppInitDLLs, &loadAppInitDLLsLen);
2185   if (status != ERROR_SUCCESS) {
2186     return false;
2187   }
2188   if (!loadAppInitDLLs) {
2189     // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
2190     // In this case we'll return true along with an empty output string.
2191     return true;
2192   }
2193   DWORD numBytes = 0;
2194   const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
2195   // Query for required buffer size
2196   status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr,
2197                             &numBytes);
2198   if (status != ERROR_SUCCESS) {
2199     return false;
2200   }
2201   // Allocate the buffer and query for the actual data
2202   mozilla::UniquePtr<wchar_t[]> data =
2203       mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
2204   status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr,
2205                             (LPBYTE)data.get(), &numBytes);
2206   if (status != ERROR_SUCCESS) {
2207     return false;
2208   }
2209   // For each token, split up the filename components and then check the
2210   // name of the file.
2211   const wchar_t kDelimiters[] = L", ";
2212   wchar_t* tokenContext = nullptr;
2213   wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
2214   while (token) {
2215     nsAutoString cleanPath(token);
2216     // Since these paths are short paths originating from the registry, we need
2217     // to canonicalize them, lengthen them, and sanitize them before we can
2218     // check them against the whitelist
2219     if (PreparePathForTelemetry(cleanPath)) {
2220       if (!aOutput.IsEmpty()) {
2221         aOutput += L";";
2222       }
2223       aOutput += cleanPath;
2224     }
2225     token = wcstok_s(nullptr, kDelimiters, &tokenContext);
2226   }
2227   return true;
2228 }
2229 
2230 /* static */
PreparePathForTelemetry(nsAString & aPath,PathTransformFlags aFlags)2231 bool WinUtils::PreparePathForTelemetry(nsAString& aPath,
2232                                        PathTransformFlags aFlags) {
2233   if (aFlags & PathTransformFlags::Canonicalize) {
2234     if (!CanonicalizePath(aPath)) {
2235       return false;
2236     }
2237   }
2238   if (aFlags & PathTransformFlags::Lengthen) {
2239     if (!MakeLongPath(aPath)) {
2240       return false;
2241     }
2242   }
2243   if (aFlags & PathTransformFlags::UnexpandEnvVars) {
2244     if (!UnexpandEnvVars(aPath)) {
2245       return false;
2246     }
2247   }
2248 
2249   const WhitelistVec& whitelistedPaths = GetWhitelistedPaths();
2250 
2251   for (uint32_t i = 0; i < whitelistedPaths.length(); ++i) {
2252     const nsString& testPath = whitelistedPaths[i].first;
2253     const nsDependentString& substitution = whitelistedPaths[i].second;
2254     if (StringBeginsWith(aPath, testPath, nsCaseInsensitiveStringComparator)) {
2255       if (!substitution.IsVoid()) {
2256         aPath.Replace(0, testPath.Length(), substitution);
2257       }
2258       return true;
2259     }
2260   }
2261 
2262   // For non-whitelisted paths, we strip the path component and just leave
2263   // the filename. We can't use nsLocalFile to do this because these paths may
2264   // begin with environment variables, and nsLocalFile doesn't like
2265   // non-absolute paths.
2266   const nsString& flatPath = PromiseFlatString(aPath);
2267   LPCWSTR leafStart = ::PathFindFileNameW(flatPath.get());
2268   ptrdiff_t cutLen = leafStart - flatPath.get();
2269   if (cutLen) {
2270     aPath.Cut(0, cutLen);
2271   } else if (aFlags & PathTransformFlags::RequireFilePath) {
2272     return false;
2273   }
2274 
2275   return true;
2276 }
2277 
2278 }  // namespace widget
2279 }  // namespace mozilla
2280