Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 #include "WinUtils.h"
9 #include "gfxPlatform.h"
10 #include "nsWindow.h"
11 #include "nsWindowDefs.h"
12 #include "KeyboardLayout.h"
13 #include "nsIDOMMouseEvent.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/DataSurfaceHelpers.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/RefPtr.h"
18 #include "mozilla/WindowsVersion.h"
20 #ifdef MOZ_LOGGING
21 #define FORCE_PR_LOG /* Allow logging in the release build */
22 #endif // MOZ_LOGGING
23 #include "prlog.h"
25 #include "nsString.h"
26 #include "nsDirectoryServiceUtils.h"
27 #include "imgIContainer.h"
28 #include "imgITools.h"
29 #include "nsStringStream.h"
30 #include "nsNetUtil.h"
31 #ifdef MOZ_PLACES
32 #include "mozIAsyncFavicons.h"
33 #endif
34 #include "nsIIconURI.h"
35 #include "nsIDownloader.h"
36 #include "nsINetUtil.h"
37 #include "nsIChannel.h"
38 #include "nsIObserver.h"
39 #include "imgIEncoder.h"
40 #include "nsIThread.h"
41 #include "MainThreadUtils.h"
42 #include "gfxColor.h"
43 #ifdef MOZ_METRO
44 #include "winrt/MetroInput.h"
45 #include "winrt/MetroUtils.h"
46 #endif // MOZ_METRO
48 #ifdef NS_ENABLE_TSF
49 #include <textstor.h>
50 #include "nsTextStore.h"
51 #endif // #ifdef NS_ENABLE_TSF
53 #ifdef PR_LOGGING
54 PRLogModuleInfo* gWindowsLog = nullptr;
55 #endif
57 using namespace mozilla::gfx;
59 namespace mozilla {
60 namespace widget {
62 NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
63 #ifdef MOZ_PLACES
64 NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
65 #endif
66 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
67 NS_IMPL_ISUPPORTS(AsyncDeleteIconFromDisk, nsIRunnable)
68 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
71 const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
72 const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
74 // apis available on vista and up.
75 WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = nullptr;
76 WinUtils::SHGetKnownFolderPathPtr WinUtils::sGetKnownFolderPath = nullptr;
78 // We just leak these DLL HMODULEs. There's no point in calling FreeLibrary
79 // on them during shutdown anyway.
80 static const wchar_t kShellLibraryName[] = L"shell32.dll";
81 static HMODULE sShellDll = nullptr;
82 static const wchar_t kDwmLibraryName[] = L"dwmapi.dll";
83 static HMODULE sDwmDll = nullptr;
85 WinUtils::DwmExtendFrameIntoClientAreaProc WinUtils::dwmExtendFrameIntoClientAreaPtr = nullptr;
86 WinUtils::DwmIsCompositionEnabledProc WinUtils::dwmIsCompositionEnabledPtr = nullptr;
87 WinUtils::DwmSetIconicThumbnailProc WinUtils::dwmSetIconicThumbnailPtr = nullptr;
88 WinUtils::DwmSetIconicLivePreviewBitmapProc WinUtils::dwmSetIconicLivePreviewBitmapPtr = nullptr;
89 WinUtils::DwmGetWindowAttributeProc WinUtils::dwmGetWindowAttributePtr = nullptr;
90 WinUtils::DwmSetWindowAttributeProc WinUtils::dwmSetWindowAttributePtr = nullptr;
91 WinUtils::DwmInvalidateIconicBitmapsProc WinUtils::dwmInvalidateIconicBitmapsPtr = nullptr;
92 WinUtils::DwmDefWindowProcProc WinUtils::dwmDwmDefWindowProcPtr = nullptr;
93 WinUtils::DwmGetCompositionTimingInfoProc WinUtils::dwmGetCompositionTimingInfoPtr = nullptr;
95 /* static */
96 void
97 WinUtils::Initialize()
98 {
99 #ifdef PR_LOGGING
100 if (!gWindowsLog) {
101 gWindowsLog = PR_NewLogModule("Widget");
102 }
103 #endif
104 if (!sDwmDll && IsVistaOrLater()) {
105 sDwmDll = ::LoadLibraryW(kDwmLibraryName);
107 if (sDwmDll) {
108 dwmExtendFrameIntoClientAreaPtr = (DwmExtendFrameIntoClientAreaProc)::GetProcAddress(sDwmDll, "DwmExtendFrameIntoClientArea");
109 dwmIsCompositionEnabledPtr = (DwmIsCompositionEnabledProc)::GetProcAddress(sDwmDll, "DwmIsCompositionEnabled");
110 dwmSetIconicThumbnailPtr = (DwmSetIconicThumbnailProc)::GetProcAddress(sDwmDll, "DwmSetIconicThumbnail");
111 dwmSetIconicLivePreviewBitmapPtr = (DwmSetIconicLivePreviewBitmapProc)::GetProcAddress(sDwmDll, "DwmSetIconicLivePreviewBitmap");
112 dwmGetWindowAttributePtr = (DwmGetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmGetWindowAttribute");
113 dwmSetWindowAttributePtr = (DwmSetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmSetWindowAttribute");
114 dwmInvalidateIconicBitmapsPtr = (DwmInvalidateIconicBitmapsProc)::GetProcAddress(sDwmDll, "DwmInvalidateIconicBitmaps");
115 dwmDwmDefWindowProcPtr = (DwmDefWindowProcProc)::GetProcAddress(sDwmDll, "DwmDefWindowProc");
116 dwmGetCompositionTimingInfoPtr = (DwmGetCompositionTimingInfoProc)::GetProcAddress(sDwmDll, "DwmGetCompositionTimingInfo");
117 }
118 }
119 }
121 // static
122 void
123 WinUtils::LogW(const wchar_t *fmt, ...)
124 {
125 va_list args = nullptr;
126 if(!lstrlenW(fmt)) {
127 return;
128 }
129 va_start(args, fmt);
130 int buflen = _vscwprintf(fmt, args);
131 wchar_t* buffer = new wchar_t[buflen+1];
132 if (!buffer) {
133 va_end(args);
134 return;
135 }
136 vswprintf(buffer, buflen, fmt, args);
137 va_end(args);
139 // MSVC, including remote debug sessions
140 OutputDebugStringW(buffer);
141 OutputDebugStringW(L"\n");
143 int len = wcslen(buffer);
144 if (len) {
145 char* utf8 = new char[len+1];
146 memset(utf8, 0, sizeof(utf8));
147 if (WideCharToMultiByte(CP_ACP, 0, buffer,
148 -1, utf8, len+1, nullptr,
149 nullptr) > 0) {
150 // desktop console
151 printf("%s\n", utf8);
152 #ifdef PR_LOGGING
153 NS_ASSERTION(gWindowsLog, "Called WinUtils Log() but Widget "
154 "log module doesn't exist!");
155 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, (utf8));
156 #endif
157 }
158 delete[] utf8;
159 }
160 delete[] buffer;
161 }
163 // static
164 void
165 WinUtils::Log(const char *fmt, ...)
166 {
167 va_list args = nullptr;
168 if(!strlen(fmt)) {
169 return;
170 }
171 va_start(args, fmt);
172 int buflen = _vscprintf(fmt, args);
173 char* buffer = new char[buflen+1];
174 if (!buffer) {
175 va_end(args);
176 return;
177 }
178 vsprintf(buffer, fmt, args);
179 va_end(args);
181 // MSVC, including remote debug sessions
182 OutputDebugStringA(buffer);
183 OutputDebugStringW(L"\n");
185 // desktop console
186 printf("%s\n", buffer);
188 #ifdef PR_LOGGING
189 NS_ASSERTION(gWindowsLog, "Called WinUtils Log() but Widget "
190 "log module doesn't exist!");
191 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, (buffer));
192 #endif
193 delete[] buffer;
194 }
196 /* static */
197 double
198 WinUtils::LogToPhysFactor()
199 {
200 // dpi / 96.0
201 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
202 #ifdef MOZ_METRO
203 return MetroUtils::LogToPhysFactor();
204 #else
205 return 1.0;
206 #endif
207 } else {
208 HDC hdc = ::GetDC(nullptr);
209 double result = ::GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
210 ::ReleaseDC(nullptr, hdc);
211 return result;
212 }
213 }
215 /* static */
216 double
217 WinUtils::PhysToLogFactor()
218 {
219 // 1.0 / (dpi / 96.0)
220 return 1.0 / LogToPhysFactor();
221 }
223 /* static */
224 double
225 WinUtils::PhysToLog(int32_t aValue)
226 {
227 return double(aValue) * PhysToLogFactor();
228 }
230 /* static */
231 int32_t
232 WinUtils::LogToPhys(double aValue)
233 {
234 return int32_t(NS_round(aValue * LogToPhysFactor()));
235 }
237 /* static */
238 bool
239 WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
240 UINT aLastMessage, UINT aOption)
241 {
242 #ifdef NS_ENABLE_TSF
243 ITfMessagePump* msgPump = nsTextStore::GetMessagePump();
244 if (msgPump) {
245 BOOL ret = FALSE;
246 HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
247 aOption, &ret);
248 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
249 return ret;
250 }
251 #endif // #ifdef NS_ENABLE_TSF
252 return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption);
253 }
255 /* static */
256 bool
257 WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
258 UINT aLastMessage)
259 {
260 #ifdef NS_ENABLE_TSF
261 ITfMessagePump* msgPump = nsTextStore::GetMessagePump();
262 if (msgPump) {
263 BOOL ret = FALSE;
264 HRESULT hr = msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
265 &ret);
266 NS_ENSURE_TRUE(SUCCEEDED(hr), false);
267 return ret;
268 }
269 #endif // #ifdef NS_ENABLE_TSF
270 return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
271 }
273 /* static */
274 bool
275 WinUtils::GetRegistryKey(HKEY aRoot,
276 char16ptr_t aKeyName,
277 char16ptr_t aValueName,
278 wchar_t* aBuffer,
279 DWORD aBufferLength)
280 {
281 NS_PRECONDITION(aKeyName, "The key name is NULL");
283 HKEY key;
284 LONG result =
285 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
286 if (result != ERROR_SUCCESS) {
287 result =
288 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
289 if (result != ERROR_SUCCESS) {
290 return false;
291 }
292 }
294 DWORD type;
295 result =
296 ::RegQueryValueExW(key, aValueName, nullptr, &type, (BYTE*) aBuffer,
297 &aBufferLength);
298 ::RegCloseKey(key);
299 if (result != ERROR_SUCCESS || type != REG_SZ) {
300 return false;
301 }
302 if (aBuffer) {
303 aBuffer[aBufferLength / sizeof(*aBuffer) - 1] = 0;
304 }
305 return true;
306 }
308 /* static */
309 bool
310 WinUtils::HasRegistryKey(HKEY aRoot, char16ptr_t aKeyName)
311 {
312 MOZ_ASSERT(aRoot, "aRoot must not be NULL");
313 MOZ_ASSERT(aKeyName, "aKeyName must not be NULL");
314 HKEY key;
315 LONG result =
316 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
317 if (result != ERROR_SUCCESS) {
318 result =
319 ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
320 if (result != ERROR_SUCCESS) {
321 return false;
322 }
323 }
324 ::RegCloseKey(key);
325 return true;
326 }
328 /* static */
329 HWND
330 WinUtils::GetTopLevelHWND(HWND aWnd,
331 bool aStopIfNotChild,
332 bool aStopIfNotPopup)
333 {
334 HWND curWnd = aWnd;
335 HWND topWnd = nullptr;
337 while (curWnd) {
338 topWnd = curWnd;
340 if (aStopIfNotChild) {
341 DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE);
343 VERIFY_WINDOW_STYLE(style);
345 if (!(style & WS_CHILD)) // first top-level window
346 break;
347 }
349 HWND upWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
351 // GetParent will only return the owner if the passed in window
352 // has the WS_POPUP style.
353 if (!upWnd && !aStopIfNotPopup) {
354 upWnd = ::GetWindow(curWnd, GW_OWNER);
355 }
356 curWnd = upWnd;
357 }
359 return topWnd;
360 }
362 static const wchar_t*
363 GetNSWindowPropName()
364 {
365 static wchar_t sPropName[40] = L"";
366 if (!*sPropName) {
367 _snwprintf(sPropName, 39, L"MozillansIWidgetPtr%p",
368 ::GetCurrentProcessId());
369 sPropName[39] = '\0';
370 }
371 return sPropName;
372 }
374 /* static */
375 bool
376 WinUtils::SetNSWindowBasePtr(HWND aWnd, nsWindowBase* aWidget)
377 {
378 if (!aWidget) {
379 ::RemovePropW(aWnd, GetNSWindowPropName());
380 return true;
381 }
382 return ::SetPropW(aWnd, GetNSWindowPropName(), (HANDLE)aWidget);
383 }
385 /* static */
386 nsWindowBase*
387 WinUtils::GetNSWindowBasePtr(HWND aWnd)
388 {
389 return static_cast<nsWindowBase*>(::GetPropW(aWnd, GetNSWindowPropName()));
390 }
392 /* static */
393 nsWindow*
394 WinUtils::GetNSWindowPtr(HWND aWnd)
395 {
396 return static_cast<nsWindow*>(::GetPropW(aWnd, GetNSWindowPropName()));
397 }
399 static BOOL CALLBACK
400 AddMonitor(HMONITOR, HDC, LPRECT, LPARAM aParam)
401 {
402 (*(int32_t*)aParam)++;
403 return TRUE;
404 }
406 /* static */
407 int32_t
408 WinUtils::GetMonitorCount()
409 {
410 int32_t monitorCount = 0;
411 EnumDisplayMonitors(nullptr, nullptr, AddMonitor, (LPARAM)&monitorCount);
412 return monitorCount;
413 }
415 /* static */
416 bool
417 WinUtils::IsOurProcessWindow(HWND aWnd)
418 {
419 if (!aWnd) {
420 return false;
421 }
422 DWORD processId = 0;
423 ::GetWindowThreadProcessId(aWnd, &processId);
424 return (processId == ::GetCurrentProcessId());
425 }
427 /* static */
428 HWND
429 WinUtils::FindOurProcessWindow(HWND aWnd)
430 {
431 for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) {
432 if (IsOurProcessWindow(wnd)) {
433 return wnd;
434 }
435 }
436 return nullptr;
437 }
439 static bool
440 IsPointInWindow(HWND aWnd, const POINT& aPointInScreen)
441 {
442 RECT bounds;
443 if (!::GetWindowRect(aWnd, &bounds)) {
444 return false;
445 }
447 return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right &&
448 aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom);
449 }
451 /**
452 * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
453 * forground in this context) of aWnd.
454 */
456 static HWND
457 FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen)
458 {
459 if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) {
460 return nullptr;
461 }
463 HWND childWnd = ::GetTopWindow(aWnd);
464 while (childWnd) {
465 HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen);
466 if (topmostWnd) {
467 return topmostWnd;
468 }
469 childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
470 }
472 return aWnd;
473 }
475 struct FindOurWindowAtPointInfo
476 {
477 POINT mInPointInScreen;
478 HWND mOutWnd;
479 };
481 static BOOL CALLBACK
482 FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM)
483 {
484 if (!WinUtils::IsOurProcessWindow(aWnd)) {
485 // This isn't one of our top-level windows; continue enumerating.
486 return TRUE;
487 }
489 // Get the top-most child window under the point. If there's no child
490 // window, and the point is within the top-level window, then the top-level
491 // window will be returned. (This is the usual case. A child window
492 // would be returned for plugins.)
493 FindOurWindowAtPointInfo* info =
494 reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
495 HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen);
496 if (!childWnd) {
497 // This window doesn't contain the point; continue enumerating.
498 return TRUE;
499 }
501 // Return the HWND and stop enumerating.
502 info->mOutWnd = childWnd;
503 return FALSE;
504 }
506 /* static */
507 HWND
508 WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen)
509 {
510 FindOurWindowAtPointInfo info;
511 info.mInPointInScreen = aPointInScreen;
512 info.mOutWnd = nullptr;
514 // This will enumerate all top-level windows in order from top to bottom.
515 EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
516 return info.mOutWnd;
517 }
519 /* static */
520 UINT
521 WinUtils::GetInternalMessage(UINT aNativeMessage)
522 {
523 switch (aNativeMessage) {
524 case WM_MOUSEWHEEL:
525 return MOZ_WM_MOUSEVWHEEL;
526 case WM_MOUSEHWHEEL:
527 return MOZ_WM_MOUSEHWHEEL;
528 case WM_VSCROLL:
529 return MOZ_WM_VSCROLL;
530 case WM_HSCROLL:
531 return MOZ_WM_HSCROLL;
532 default:
533 return aNativeMessage;
534 }
535 }
537 /* static */
538 UINT
539 WinUtils::GetNativeMessage(UINT aInternalMessage)
540 {
541 switch (aInternalMessage) {
542 case MOZ_WM_MOUSEVWHEEL:
543 return WM_MOUSEWHEEL;
544 case MOZ_WM_MOUSEHWHEEL:
545 return WM_MOUSEHWHEEL;
546 case MOZ_WM_VSCROLL:
547 return WM_VSCROLL;
548 case MOZ_WM_HSCROLL:
549 return WM_HSCROLL;
550 default:
551 return aInternalMessage;
552 }
553 }
555 /* static */
556 uint16_t
557 WinUtils::GetMouseInputSource()
558 {
559 int32_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
560 LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
561 if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
562 inputSource = (lParamExtraInfo & TABLET_INK_TOUCH) ?
563 nsIDOMMouseEvent::MOZ_SOURCE_TOUCH : nsIDOMMouseEvent::MOZ_SOURCE_PEN;
564 }
565 return static_cast<uint16_t>(inputSource);
566 }
568 bool
569 WinUtils::GetIsMouseFromTouch(uint32_t aEventType)
570 {
571 #define MOUSEEVENTF_FROMTOUCH 0xFF515700
572 return (aEventType == NS_MOUSE_BUTTON_DOWN ||
573 aEventType == NS_MOUSE_BUTTON_UP ||
574 aEventType == NS_MOUSE_MOVE) &&
575 (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH);
576 }
578 /* static */
579 MSG
580 WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd)
581 {
582 MSG msg;
583 msg.message = aMessage;
584 msg.wParam = wParam;
585 msg.lParam = lParam;
586 msg.hwnd = aWnd;
587 return msg;
588 }
590 /* static */
591 HRESULT
592 WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc,
593 REFIID riid, void **ppv)
594 {
595 if (sCreateItemFromParsingName) {
596 return sCreateItemFromParsingName(pszPath, pbc, riid, ppv);
597 }
599 if (!sShellDll) {
600 sShellDll = ::LoadLibraryW(kShellLibraryName);
601 if (!sShellDll) {
602 return false;
603 }
604 }
606 sCreateItemFromParsingName = (SHCreateItemFromParsingNamePtr)
607 GetProcAddress(sShellDll, "SHCreateItemFromParsingName");
608 if (!sCreateItemFromParsingName)
609 return E_FAIL;
611 return sCreateItemFromParsingName(pszPath, pbc, riid, ppv);
612 }
614 /* static */
615 HRESULT
616 WinUtils::SHGetKnownFolderPath(REFKNOWNFOLDERID rfid,
617 DWORD dwFlags,
618 HANDLE hToken,
619 PWSTR *ppszPath)
620 {
621 if (sGetKnownFolderPath) {
622 return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
623 }
625 if (!sShellDll) {
626 sShellDll = ::LoadLibraryW(kShellLibraryName);
627 if (!sShellDll) {
628 return false;
629 }
630 }
632 sGetKnownFolderPath = (SHGetKnownFolderPathPtr)
633 GetProcAddress(sShellDll, "SHGetKnownFolderPath");
634 if (!sGetKnownFolderPath)
635 return E_FAIL;
637 return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
638 }
640 #ifdef MOZ_PLACES
641 /************************************************************************/
642 /* Constructs as AsyncFaviconDataReady Object
643 /* @param aIOThread : the thread which performs the action
644 /* @param aURLShortcut : Differentiates between (false)Jumplistcache and (true)Shortcutcache
645 /************************************************************************/
647 AsyncFaviconDataReady::AsyncFaviconDataReady(nsIURI *aNewURI,
648 nsCOMPtr<nsIThread> &aIOThread,
649 const bool aURLShortcut):
650 mNewURI(aNewURI),
651 mIOThread(aIOThread),
652 mURLShortcut(aURLShortcut)
653 {
654 }
656 NS_IMETHODIMP
657 myDownloadObserver::OnDownloadComplete(nsIDownloader *downloader,
658 nsIRequest *request,
659 nsISupports *ctxt,
660 nsresult status,
661 nsIFile *result)
662 {
663 return NS_OK;
664 }
666 nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void)
667 {
668 if (!mURLShortcut) {
669 return NS_OK;
670 }
672 nsCOMPtr<nsIFile> icoFile;
673 nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
674 NS_ENSURE_SUCCESS(rv, rv);
676 nsCOMPtr<nsIURI> mozIconURI;
677 rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
678 if (NS_FAILED(rv)) {
679 return rv;
680 }
682 nsCOMPtr<nsIChannel> channel;
683 rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI);
684 NS_ENSURE_SUCCESS(rv, rv);
686 nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
687 nsCOMPtr<nsIStreamListener> listener;
688 rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
689 NS_ENSURE_SUCCESS(rv, rv);
691 channel->AsyncOpen(listener, nullptr);
692 return NS_OK;
693 }
695 NS_IMETHODIMP
696 AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI,
697 uint32_t aDataLen,
698 const uint8_t *aData,
699 const nsACString &aMimeType)
700 {
701 if (!aDataLen || !aData) {
702 if (mURLShortcut) {
703 OnFaviconDataNotAvailable();
704 }
706 return NS_OK;
707 }
709 nsCOMPtr<nsIFile> icoFile;
710 nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
711 NS_ENSURE_SUCCESS(rv, rv);
713 nsAutoString path;
714 rv = icoFile->GetPath(path);
715 NS_ENSURE_SUCCESS(rv, rv);
717 // Convert the obtained favicon data to an input stream
718 nsCOMPtr<nsIInputStream> stream;
719 rv = NS_NewByteInputStream(getter_AddRefs(stream),
720 reinterpret_cast<const char*>(aData),
721 aDataLen,
722 NS_ASSIGNMENT_DEPEND);
723 NS_ENSURE_SUCCESS(rv, rv);
725 // Decode the image from the format it was returned to us in (probably PNG)
726 nsAutoCString mimeTypeOfInputData;
727 mimeTypeOfInputData.AssignLiteral("image/vnd.microsoft.icon");
728 nsCOMPtr<imgIContainer> container;
729 nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
730 rv = imgtool->DecodeImageData(stream, aMimeType,
731 getter_AddRefs(container));
732 NS_ENSURE_SUCCESS(rv, rv);
734 RefPtr<SourceSurface> surface =
735 container->GetFrame(imgIContainer::FRAME_FIRST, 0);
736 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
738 RefPtr<DataSourceSurface> dataSurface;
739 IntSize size;
741 if (mURLShortcut) {
742 // Create a 48x48 surface and paint the icon into the central 16x16 rect.
743 size.width = 48;
744 size.height = 48;
745 dataSurface =
746 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
747 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
749 DataSourceSurface::MappedSurface map;
750 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
751 return NS_ERROR_FAILURE;
752 }
754 RefPtr<DrawTarget> dt =
755 Factory::CreateDrawTargetForData(BackendType::CAIRO,
756 map.mData,
757 dataSurface->GetSize(),
758 map.mStride,
759 dataSurface->GetFormat());
760 dt->FillRect(Rect(0, 0, size.width, size.height),
761 ColorPattern(Color(1.0f, 1.0f, 1.0f, 1.0f)));
762 dt->DrawSurface(surface,
763 Rect(16, 16, 16, 16),
764 Rect(Point(0, 0),
765 Size(surface->GetSize().width, surface->GetSize().height)));
767 dataSurface->Unmap();
768 } else {
769 // By using the input image surface's size, we may end up encoding
770 // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
771 // Windows will resize appropriately for us. If we want to encode ourselves
772 // one day because we like our resizing better, we'd have to manually
773 // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
774 // SM_CYSMICON. We don't support resizing images asynchronously at the
775 // moment anyway so getting the DPI aware icon size won't help.
776 size.width = surface->GetSize().width;
777 size.height = surface->GetSize().height;
778 dataSurface = surface->GetDataSurface();
779 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
780 }
782 // Allocate a new buffer that we own and can use out of line in
783 // another thread.
784 uint8_t *data = SurfaceToPackedBGRA(dataSurface);
785 if (!data) {
786 return NS_ERROR_OUT_OF_MEMORY;
787 }
788 int32_t stride = 4 * size.width;
789 int32_t dataLength = stride * size.height;
791 // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
792 nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(path, data,
793 dataLength,
794 stride,
795 size.width,
796 size.height,
797 mURLShortcut);
798 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
800 return NS_OK;
801 }
802 #endif
804 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed in
805 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(const nsAString &aIconPath,
806 uint8_t *aBuffer,
807 uint32_t aBufferLength,
808 uint32_t aStride,
809 uint32_t aWidth,
810 uint32_t aHeight,
811 const bool aURLShortcut) :
812 mURLShortcut(aURLShortcut),
813 mIconPath(aIconPath),
814 mBuffer(aBuffer),
815 mBufferLength(aBufferLength),
816 mStride(aStride),
817 mWidth(aWidth),
818 mHeight(aHeight)
819 {
820 }
822 NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run()
823 {
824 NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
826 nsCOMPtr<nsIInputStream> iconStream;
827 nsRefPtr<imgIEncoder> encoder =
828 do_CreateInstance("@mozilla.org/image/encoder;2?"
829 "type=image/vnd.microsoft.icon");
830 NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
831 nsresult rv = encoder->InitFromData(mBuffer, mBufferLength,
832 mWidth, mHeight,
833 mStride,
834 imgIEncoder::INPUT_FORMAT_HOSTARGB,
835 EmptyString());
836 NS_ENSURE_SUCCESS(rv, rv);
837 CallQueryInterface(encoder.get(), getter_AddRefs(iconStream));
838 if (!iconStream) {
839 return NS_ERROR_FAILURE;
840 }
842 NS_ENSURE_SUCCESS(rv, rv);
843 nsCOMPtr<nsIFile> icoFile
844 = do_CreateInstance("@mozilla.org/file/local;1");
845 NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
846 rv = icoFile->InitWithPath(mIconPath);
848 // Try to create the directory if it's not there yet
849 nsCOMPtr<nsIFile> dirPath;
850 icoFile->GetParent(getter_AddRefs(dirPath));
851 rv = (dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777));
852 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
853 return rv;
854 }
856 // Setup the output stream for the ICO file on disk
857 nsCOMPtr<nsIOutputStream> outputStream;
858 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile);
859 NS_ENSURE_SUCCESS(rv, rv);
861 // Obtain the ICO buffer size from the re-encoded ICO stream
862 uint64_t bufSize64;
863 rv = iconStream->Available(&bufSize64);
864 NS_ENSURE_SUCCESS(rv, rv);
865 NS_ENSURE_TRUE(bufSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
867 uint32_t bufSize = (uint32_t)bufSize64;
869 // Setup a buffered output stream from the stream object
870 // so that we can simply use WriteFrom with the stream object
871 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
872 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
873 outputStream, bufSize);
874 NS_ENSURE_SUCCESS(rv, rv);
876 // Write out the icon stream to disk and make sure we wrote everything
877 uint32_t wrote;
878 rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote);
879 NS_ASSERTION(bufSize == wrote,
880 "Icon wrote size should be equal to requested write size");
882 // Cleanup
883 bufferedOutputStream->Close();
884 outputStream->Close();
885 if (mURLShortcut) {
886 SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0);
887 }
888 return rv;
889 }
891 AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon()
892 {
893 }
895 AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath)
896 : mIconPath(aIconPath)
897 {
898 }
900 NS_IMETHODIMP AsyncDeleteIconFromDisk::Run()
901 {
902 // Construct the parent path of the passed in path
903 nsCOMPtr<nsIFile> icoFile = do_CreateInstance("@mozilla.org/file/local;1");
904 NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
905 nsresult rv = icoFile->InitWithPath(mIconPath);
906 NS_ENSURE_SUCCESS(rv, rv);
908 // Check if the cached ICO file exists
909 bool exists;
910 rv = icoFile->Exists(&exists);
911 NS_ENSURE_SUCCESS(rv, rv);
913 // Check that we aren't deleting some arbitrary file that is not an icon
914 if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) {
915 // Check if the cached ICO file exists
916 bool exists;
917 if (NS_FAILED(icoFile->Exists(&exists)) || !exists)
918 return NS_ERROR_FAILURE;
920 // We found an ICO file that exists, so we should remove it
921 icoFile->Remove(false);
922 }
924 return NS_OK;
925 }
927 AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk()
928 {
929 }
931 AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk()
932 {
933 }
935 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run()
936 {
937 // Construct the path of our jump list cache
938 nsCOMPtr<nsIFile> jumpListCacheDir;
939 nsresult rv = NS_GetSpecialDirectory("ProfLDS",
940 getter_AddRefs(jumpListCacheDir));
941 NS_ENSURE_SUCCESS(rv, rv);
942 rv = jumpListCacheDir->AppendNative(
943 nsDependentCString(FaviconHelper::kJumpListCacheDir));
944 NS_ENSURE_SUCCESS(rv, rv);
945 nsCOMPtr<nsISimpleEnumerator> entries;
946 rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
947 NS_ENSURE_SUCCESS(rv, rv);
949 // Loop through each directory entry and remove all ICO files found
950 do {
951 bool hasMore = false;
952 if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore)
953 break;
955 nsCOMPtr<nsISupports> supp;
956 if (NS_FAILED(entries->GetNext(getter_AddRefs(supp))))
957 break;
959 nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
960 nsAutoString path;
961 if (NS_FAILED(currFile->GetPath(path)))
962 continue;
964 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
965 // Check if the cached ICO file exists
966 bool exists;
967 if (NS_FAILED(currFile->Exists(&exists)) || !exists)
968 continue;
970 // We found an ICO file that exists, so we should remove it
971 currFile->Remove(false);
972 }
973 } while(true);
975 return NS_OK;
976 }
978 AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk()
979 {
980 }
983 /*
984 * (static) If the data is available, will return the path on disk where
985 * the favicon for page aFaviconPageURI is stored. If the favicon does not
986 * exist, or its cache is expired, this method will kick off an async request
987 * for the icon so that next time the method is called it will be available.
988 * @param aFaviconPageURI The URI of the page to obtain
989 * @param aICOFilePath The path of the icon file
990 * @param aIOThread The thread to perform the Fetch on
991 * @param aURLShortcut to distinguish between jumplistcache(false) and shortcutcache(true)
992 */
993 nsresult FaviconHelper::ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
994 nsString &aICOFilePath,
995 nsCOMPtr<nsIThread> &aIOThread,
996 bool aURLShortcut)
997 {
998 // Obtain the ICO file path
999 nsCOMPtr<nsIFile> icoFile;
1000 nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
1001 NS_ENSURE_SUCCESS(rv, rv);
1003 // Check if the cached ICO file already exists
1004 bool exists;
1005 rv = icoFile->Exists(&exists);
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 if (exists) {
1010 // Obtain the file's last modification date in seconds
1011 int64_t fileModTime = 0;
1012 rv = icoFile->GetLastModifiedTime(&fileModTime);
1013 fileModTime /= PR_MSEC_PER_SEC;
1014 int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
1015 int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
1017 // If the last mod call failed or the icon is old then re-cache it
1018 // This check is in case the favicon of a page changes
1019 // the next time we try to build the jump list, the data will be available.
1020 if (NS_FAILED(rv) ||
1021 (nowTime - fileModTime) > icoReCacheSecondsTimeout) {
1022 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut);
1023 return NS_ERROR_NOT_AVAILABLE;
1024 }
1025 } else {
1027 // The file does not exist yet, obtain it async from the favicon service so that
1028 // the next time we try to build the jump list it'll be available.
1029 CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut);
1030 return NS_ERROR_NOT_AVAILABLE;
1031 }
1033 // The icoFile is filled with a path that exists, get its path
1034 rv = icoFile->GetPath(aICOFilePath);
1035 return rv;
1036 }
1038 nsresult FaviconHelper::HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash,
1039 nsIURI *aUri,
1040 nsACString& aUriHash)
1041 {
1042 if (!aUri)
1043 return NS_ERROR_INVALID_ARG;
1045 nsAutoCString spec;
1046 nsresult rv = aUri->GetSpec(spec);
1047 NS_ENSURE_SUCCESS(rv, rv);
1049 if (!aCryptoHash) {
1050 aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1051 NS_ENSURE_SUCCESS(rv, rv);
1052 }
1054 rv = aCryptoHash->Init(nsICryptoHash::MD5);
1055 NS_ENSURE_SUCCESS(rv, rv);
1056 rv = aCryptoHash->Update(reinterpret_cast<const uint8_t*>(spec.BeginReading()),
1057 spec.Length());
1058 NS_ENSURE_SUCCESS(rv, rv);
1059 rv = aCryptoHash->Finish(true, aUriHash);
1060 NS_ENSURE_SUCCESS(rv, rv);
1062 return NS_OK;
1063 }
1067 // (static) Obtains the ICO file for the favicon at page aFaviconPageURI
1068 // If successful, the file path on disk is in the format:
1069 // <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
1070 nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
1071 nsCOMPtr<nsIFile> &aICOFile,
1072 bool aURLShortcut)
1073 {
1074 // Hash the input URI and replace any / with _
1075 nsAutoCString inputURIHash;
1076 nsCOMPtr<nsICryptoHash> cryptoHash;
1077 nsresult rv = HashURI(cryptoHash, aFaviconPageURI,
1078 inputURIHash);
1079 NS_ENSURE_SUCCESS(rv, rv);
1080 char* cur = inputURIHash.BeginWriting();
1081 char* end = inputURIHash.EndWriting();
1082 for (; cur < end; ++cur) {
1083 if ('/' == *cur) {
1084 *cur = '_';
1085 }
1086 }
1088 // Obtain the local profile directory and construct the output icon file path
1089 rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
1090 NS_ENSURE_SUCCESS(rv, rv);
1091 if (!aURLShortcut)
1092 rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
1093 else
1094 rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
1095 NS_ENSURE_SUCCESS(rv, rv);
1097 // Append the icon extension
1098 inputURIHash.Append(".ico");
1099 rv = aICOFile->AppendNative(inputURIHash);
1101 return rv;
1102 }
1104 // (static) Asynchronously creates a cached ICO file on disk for the favicon of
1105 // page aFaviconPageURI and stores it to disk at the path of aICOFile.
1106 nsresult
1107 FaviconHelper::CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI,
1108 nsCOMPtr<nsIFile> aICOFile,
1109 nsCOMPtr<nsIThread> &aIOThread,
1110 bool aURLShortcut)
1111 {
1112 #ifdef MOZ_PLACES
1113 // Obtain the favicon service and get the favicon for the specified page
1114 nsCOMPtr<mozIAsyncFavicons> favIconSvc(
1115 do_GetService("@mozilla.org/browser/favicon-service;1"));
1116 NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
1118 nsCOMPtr<nsIFaviconDataCallback> callback =
1119 new mozilla::widget::AsyncFaviconDataReady(aFaviconPageURI,
1120 aIOThread,
1121 aURLShortcut);
1123 favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback);
1124 #endif
1125 return NS_OK;
1126 }
1128 // Obtains the jump list 'ICO cache timeout in seconds' pref
1129 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
1131 // Only obtain the setting at most once from the pref service.
1132 // In the rare case that 2 threads call this at the same
1133 // time it is no harm and we will simply obtain the pref twice.
1134 // None of the taskbar list prefs are currently updated via a
1135 // pref observer so I think this should suffice.
1136 const int32_t kSecondsPerDay = 86400;
1137 static bool alreadyObtained = false;
1138 static int32_t icoReCacheSecondsTimeout = kSecondsPerDay;
1139 if (alreadyObtained) {
1140 return icoReCacheSecondsTimeout;
1141 }
1143 // Obtain the pref
1144 const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
1145 icoReCacheSecondsTimeout = Preferences::GetInt(PREF_ICOTIMEOUT,
1146 kSecondsPerDay);
1147 alreadyObtained = true;
1148 return icoReCacheSecondsTimeout;
1149 }
1154 /* static */
1155 bool
1156 WinUtils::GetShellItemPath(IShellItem* aItem,
1157 nsString& aResultString)
1158 {
1159 NS_ENSURE_TRUE(aItem, false);
1160 LPWSTR str = nullptr;
1161 if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str)))
1162 return false;
1163 aResultString.Assign(str);
1164 CoTaskMemFree(str);
1165 return !aResultString.IsEmpty();
1166 }
1168 /* static */
1169 nsIntRegion
1170 WinUtils::ConvertHRGNToRegion(HRGN aRgn)
1171 {
1172 NS_ASSERTION(aRgn, "Don't pass NULL region here");
1174 nsIntRegion rgn;
1176 DWORD size = ::GetRegionData(aRgn, 0, nullptr);
1177 nsAutoTArray<uint8_t,100> buffer;
1178 buffer.SetLength(size);
1180 RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements());
1181 if (!::GetRegionData(aRgn, size, data))
1182 return rgn;
1184 if (data->rdh.nCount > MAX_RECTS_IN_REGION) {
1185 rgn = ToIntRect(data->rdh.rcBound);
1186 return rgn;
1187 }
1189 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
1190 for (uint32_t i = 0; i < data->rdh.nCount; ++i) {
1191 RECT* r = rects + i;
1192 rgn.Or(rgn, ToIntRect(*r));
1193 }
1195 return rgn;
1196 }
1198 nsIntRect
1199 WinUtils::ToIntRect(const RECT& aRect)
1200 {
1201 return nsIntRect(aRect.left, aRect.top,
1202 aRect.right - aRect.left,
1203 aRect.bottom - aRect.top);
1204 }
1206 /* static */
1207 bool
1208 WinUtils::IsIMEEnabled(const InputContext& aInputContext)
1209 {
1210 return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
1211 }
1213 /* static */
1214 bool
1215 WinUtils::IsIMEEnabled(IMEState::Enabled aIMEState)
1216 {
1217 return (aIMEState == IMEState::ENABLED ||
1218 aIMEState == IMEState::PLUGIN);
1219 }
1221 /* static */
1222 void
1223 WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray,
1224 uint32_t aModifiers)
1225 {
1226 for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) {
1227 const uint32_t* map = sModifierKeyMap[i];
1228 if (aModifiers & map[0]) {
1229 aArray->AppendElement(KeyPair(map[1], map[2]));
1230 }
1231 }
1232 }
1234 /* static */
1235 bool
1236 WinUtils::ShouldHideScrollbars()
1237 {
1238 #ifdef MOZ_METRO
1239 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
1240 return widget::winrt::MetroInput::IsInputModeImprecise();
1241 }
1242 #endif // MOZ_METRO
1243 return false;
1244 }
1246 } // namespace widget
1247 } // namespace mozilla