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