Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/matricesforhomalg/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 7.8.2025 mit Größe 691 B image not shown  

Quelle  WinUtils.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sts=2 sw=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "WinUtils.h"

#include <knownfolders.h>
#include <psapi.h>
#include <winioctl.h>

#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "nsWindow.h"
#include "nsWindowDefs.h"
#include "InputDeviceUtils.h"
#include "KeyboardLayout.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/gfx/DisplayConfigWindows.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProfilerThreadSleep.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include "mozilla/Unused.h"
#include "nsIContentPolicy.h"
#include "WindowsUIUtils.h"
#include "nsContentUtils.h"

#include "mozilla/Logging.h"

#include "nsString.h"
#include "nsDirectoryServiceUtils.h"
#include "imgIContainer.h"
#include "imgITools.h"
#include "nsNetUtil.h"
#include "nsIOutputStream.h"
#include "nsNetCID.h"
#include "prtime.h"
#ifdef MOZ_PLACES
#  include "nsIFaviconService.h"
#endif
#include "nsIDownloader.h"
#include "nsIChannel.h"
#include "nsIThread.h"
#include "MainThreadUtils.h"
#include "nsLookAndFeel.h"
#include "nsUnicharUtils.h"
#include "nsWindowsHelpers.h"
#include "WinWindowOcclusionTracker.h"

#include <textstor.h>
#include "TSFTextStore.h"

#include <shellscalingapi.h>
#include <shlobj.h>
#include <shlwapi.h>

mozilla::LazyLogModule gWindowsLog("Widget");

using namespace mozilla::gfx;

namespace mozilla {
namespace widget {

#ifdef MOZ_PLACES
NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
#endif
NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)

const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";

struct CoTaskMemFreePolicy {
  void operator()(void* aPtr) { ::CoTaskMemFree(aPtr); }
};

SetThreadDpiAwarenessContextProc WinUtils::sSetThreadDpiAwarenessContext = NULL;
EnableNonClientDpiScalingProc WinUtils::sEnableNonClientDpiScaling = NULL;
GetSystemMetricsForDpiProc WinUtils::sGetSystemMetricsForDpi = NULL;
bool WinUtils::sHasPackageIdentity = false;

using GetDpiForWindowProc = UINT(WINAPI*)(HWND);
static GetDpiForWindowProc sGetDpiForWindow = NULL;

/* static */
void WinUtils::Initialize() {
  // Dpi-Awareness is not supported with Win32k Lockdown enabled, so we don't
  // initialize DPI-related members and assert later that nothing accidently
  // uses these static members
  if (!IsWin32kLockedDown()) {
    HMODULE user32Dll = ::GetModuleHandleW(L"user32");
    if (user32Dll) {
      auto getThreadDpiAwarenessContext =
          (decltype(GetThreadDpiAwarenessContext)*)::GetProcAddress(
              user32Dll, "GetThreadDpiAwarenessContext");
      auto areDpiAwarenessContextsEqual =
          (decltype(AreDpiAwarenessContextsEqual)*)::GetProcAddress(
              user32Dll, "AreDpiAwarenessContextsEqual");
      if (getThreadDpiAwarenessContext && areDpiAwarenessContextsEqual &&
          areDpiAwarenessContextsEqual(
              getThreadDpiAwarenessContext(),
              DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
        // Only per-monitor v1 requires these workarounds.
        sEnableNonClientDpiScaling =
            (EnableNonClientDpiScalingProc)::GetProcAddress(
                user32Dll, "EnableNonClientDpiScaling");
        sSetThreadDpiAwarenessContext =
            (SetThreadDpiAwarenessContextProc)::GetProcAddress(
                user32Dll, "SetThreadDpiAwarenessContext");
      }

      sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)::GetProcAddress(
          user32Dll, "GetSystemMetricsForDpi");
      sGetDpiForWindow =
          (GetDpiForWindowProc)::GetProcAddress(user32Dll, "GetDpiForWindow");
    }
  }

  sHasPackageIdentity = mozilla::HasPackageIdentity();
}

// static
LRESULT WINAPI WinUtils::NonClientDpiScalingDefWindowProcW(HWND hWnd, UINT msg,
                                                           WPARAM wParam,
                                                           LPARAM lParam) {
  MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());

  // NOTE: this function was copied out into the body of the pre-XUL skeleton
  // UI window proc (PreXULSkeletonUI.cpp). If this function changes at any
  // point, we should probably factor this out and use it from both locations.
  if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) {
    sEnableNonClientDpiScaling(hWnd);
  }
  return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}

// static
void WinUtils::LogW(const wchar_t* fmt, ...) {
  va_list args = nullptr;
  if (!lstrlenW(fmt)) {
    return;
  }
  va_start(args, fmt);
  int buflen = _vscwprintf(fmt, args);
  wchar_t* buffer = new wchar_t[buflen + 1];
  if (!buffer) {
    va_end(args);
    return;
  }
  vswprintf(buffer, buflen, fmt, args);
  va_end(args);

  // MSVC, including remote debug sessions
  OutputDebugStringW(buffer);
  OutputDebugStringW(L"\n");

  int len =
      WideCharToMultiByte(CP_ACP, 0, buffer, -1, nullptr, 0, nullptr, nullptr);
  if (len) {
    char* utf8 = new char[len];
    if (WideCharToMultiByte(CP_ACP, 0, buffer, -1, utf8, len, nullptr,
                            nullptr) > 0) {
      // desktop console
      printf("%s\n", utf8);
      NS_ASSERTION(gWindowsLog,
                   "Called WinUtils Log() but Widget "
                   "log module doesn't exist!");
      MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", utf8));
    }
    delete[] utf8;
  }
  delete[] buffer;
}

// static
void WinUtils::Log(const char* fmt, ...) {
  va_list args = nullptr;
  if (!strlen(fmt)) {
    return;
  }
  va_start(args, fmt);
  int buflen = _vscprintf(fmt, args);
  char* buffer = new char[buflen + 1];
  if (!buffer) {
    va_end(args);
    return;
  }
  vsprintf(buffer, fmt, args);
  va_end(args);

  // MSVC, including remote debug sessions
  OutputDebugStringA(buffer);
  OutputDebugStringW(L"\n");

  // desktop console
  printf("%s\n", buffer);

  NS_ASSERTION(gWindowsLog,
               "Called WinUtils Log() but Widget "
               "log module doesn't exist!");
  MOZ_LOG(gWindowsLog, LogLevel::Error, ("%s", buffer));
  delete[] buffer;
}

// static
float WinUtils::SystemDPI() {
  // The result of GetDeviceCaps won't change dynamically, as it predates
  // per-monitor DPI and support for on-the-fly resolution changes.
  // Therefore, we only need to look it up once.
  static float dpi = 0;
  if (dpi <= 0) {
    HDC screenDC = GetDC(nullptr);
    dpi = GetDeviceCaps(screenDC, LOGPIXELSY);
    ReleaseDC(nullptr, screenDC);
  }

  // Bug 1012487 - dpi can be 0 when the Screen DC is used off the
  // main thread on windows. For now just assume a 100% DPI for this
  // drawing call.
  // XXX - fixme!
  return dpi > 0 ? dpi : 96;
}

// static
double WinUtils::SystemScaleFactor() { return SystemDPI() / 96.0; }

typedef HRESULT(WINAPI* GETDPIFORMONITORPROC)(HMONITOR, MONITOR_DPI_TYPE, UINT*,
                                              UINT*);

typedef HRESULT(WINAPI* GETPROCESSDPIAWARENESSPROC)(HANDLE,
                                                    PROCESS_DPI_AWARENESS*);

GETDPIFORMONITORPROC sGetDpiForMonitor;
GETPROCESSDPIAWARENESSPROC sGetProcessDpiAwareness;

static bool SlowIsPerMonitorDPIAware() {
  // Intentionally leak the handle.
  HMODULE shcore = LoadLibraryEx(L"shcore", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
  if (shcore) {
    sGetDpiForMonitor =
        (GETDPIFORMONITORPROC)GetProcAddress(shcore, "GetDpiForMonitor");
    sGetProcessDpiAwareness = (GETPROCESSDPIAWARENESSPROC)GetProcAddress(
        shcore, "GetProcessDpiAwareness");
  }
  PROCESS_DPI_AWARENESS dpiAwareness;
  return sGetDpiForMonitor && sGetProcessDpiAwareness &&
         SUCCEEDED(
             sGetProcessDpiAwareness(GetCurrentProcess(), &dpiAwareness)) &&
         dpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE;
}

/* static */
bool WinUtils::IsPerMonitorDPIAware() {
  static bool perMonitorDPIAware = SlowIsPerMonitorDPIAware();
  return perMonitorDPIAware;
}

/* static */
float WinUtils::MonitorDPI(HMONITOR aMonitor) {
  if (IsPerMonitorDPIAware()) {
    UINT dpiX, dpiY = 96;
    sGetDpiForMonitor(aMonitor ? aMonitor : GetPrimaryMonitor(),
                      MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
    return dpiY;
  }

  // We're not per-monitor aware, use system DPI instead.
  return SystemDPI();
}

/* static */
double WinUtils::LogToPhysFactor(HMONITOR aMonitor) {
  return MonitorDPI(aMonitor) / 96.0;
}

/* static */
int32_t WinUtils::LogToPhys(HMONITOR aMonitor, double aValue) {
  return int32_t(NS_round(aValue * LogToPhysFactor(aMonitor)));
}

/* static */
double WinUtils::LogToPhysFactor(HWND aWnd) {
  // if there's an ancestor window, we want to share its DPI setting
  HWND ancestor = ::GetAncestor(aWnd, GA_ROOTOWNER);

  // The GetDpiForWindow api is not available everywhere where we run as
  // per-monitor, but if it is available rely on it to tell us the scale
  // factor of the window.  See bug 1722085.
  if (sGetDpiForWindow) {
    UINT dpi = sGetDpiForWindow(ancestor ? ancestor : aWnd);
    if (dpi > 0) {
      return static_cast<double>(dpi) / 96.0;
    }
  }
  return LogToPhysFactor(::MonitorFromWindow(ancestor ? ancestor : aWnd,
                                             MONITOR_DEFAULTTOPRIMARY));
}

/* static */
HMONITOR
WinUtils::GetPrimaryMonitor() {
  const POINT pt = {0, 0};
  return ::MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
}

/* static */
HMONITOR
WinUtils::MonitorFromRect(const gfx::Rect& rect) {
  // convert coordinates from desktop to device pixels for MonitorFromRect
  double dpiScale =
      IsPerMonitorDPIAware() ? 1.0 : LogToPhysFactor(GetPrimaryMonitor());

  RECT globalWindowBounds = {NSToIntRound(dpiScale * rect.X()),
                             NSToIntRound(dpiScale * rect.Y()),
                             NSToIntRound(dpiScale * (rect.XMost())),
                             NSToIntRound(dpiScale * (rect.YMost()))};

  return ::MonitorFromRect(&globalWindowBounds, MONITOR_DEFAULTTONEAREST);
}

/* static */
bool WinUtils::HasSystemMetricsForDpi() {
  MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
  return (sGetSystemMetricsForDpi != NULL);
}

/* static */
int WinUtils::GetSystemMetricsForDpi(int nIndex, UINT dpi) {
  MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
  if (HasSystemMetricsForDpi()) {
    return sGetSystemMetricsForDpi(nIndex, dpi);
  } else {
    double scale = IsPerMonitorDPIAware() ? dpi / SystemDPI() : 1.0;
    return NSToIntRound(::GetSystemMetrics(nIndex) * scale);
  }
}

/* static */
gfx::MarginDouble WinUtils::GetUnwriteableMarginsForDeviceInInches(HDC aHdc) {
  if (!aHdc) {
    return gfx::MarginDouble();
  }

  int pixelsPerInchY = ::GetDeviceCaps(aHdc, LOGPIXELSY);
  int marginTop = ::GetDeviceCaps(aHdc, PHYSICALOFFSETY);
  int printableAreaHeight = ::GetDeviceCaps(aHdc, VERTRES);
  int physicalHeight = ::GetDeviceCaps(aHdc, PHYSICALHEIGHT);

  double marginTopInch = double(marginTop) / pixelsPerInchY;

  double printableAreaHeightInch = double(printableAreaHeight) / pixelsPerInchY;
  double physicalHeightInch = double(physicalHeight) / pixelsPerInchY;
  double marginBottomInch =
      physicalHeightInch - printableAreaHeightInch - marginTopInch;

  int pixelsPerInchX = ::GetDeviceCaps(aHdc, LOGPIXELSX);
  int marginLeft = ::GetDeviceCaps(aHdc, PHYSICALOFFSETX);
  int printableAreaWidth = ::GetDeviceCaps(aHdc, HORZRES);
  int physicalWidth = ::GetDeviceCaps(aHdc, PHYSICALWIDTH);

  double marginLeftInch = double(marginLeft) / pixelsPerInchX;

  double printableAreaWidthInch = double(printableAreaWidth) / pixelsPerInchX;
  double physicalWidthInch = double(physicalWidth) / pixelsPerInchX;
  double marginRightInch =
      physicalWidthInch - printableAreaWidthInch - marginLeftInch;

  return gfx::MarginDouble(marginTopInch, marginRightInch, marginBottomInch,
                           marginLeftInch);
}

#ifdef ACCESSIBILITY
/* static */
a11y::LocalAccessible* WinUtils::GetRootAccessibleForHWND(HWND aHwnd) {
  nsWindow* window = GetNSWindowPtr(aHwnd);
  if (!window) {
    return nullptr;
  }

  return window->GetAccessible();
}
#endif  // ACCESSIBILITY

/* static */
bool WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
                           UINT aLastMessage, UINT aOption) {
  RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
  if (msgPump) {
    BOOL ret = FALSE;
    HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
                                       aOption, &ret);
    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
    return ret;
  }
  return ::PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, aOption);
}

/* static */
bool WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
                          UINT aLastMessage) {
  RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
  if (msgPump) {
    BOOL ret = FALSE;
    HRESULT hr =
        msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage, &ret);
    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
    return ret;
  }
  return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
}

#if defined(ACCESSIBILITY)
static DWORD GetWaitFlags() {
  DWORD result = MWMO_INPUTAVAILABLE;
  if (XRE_IsContentProcess()) {
    result |= MWMO_ALERTABLE;
  }
  return result;
}
#endif

/* static */
void WinUtils::WaitForMessage(DWORD aTimeoutMs) {
#if defined(ACCESSIBILITY)
  static const DWORD waitFlags = GetWaitFlags();
#else
  const DWORD waitFlags = MWMO_INPUTAVAILABLE;
#endif

  const DWORD waitStart = ::GetTickCount();
  DWORD elapsed = 0;
  while (true) {
    if (aTimeoutMs != INFINITE) {
      elapsed = ::GetTickCount() - waitStart;
    }
    if (elapsed >= aTimeoutMs) {
      break;
    }
    DWORD result;
    {
      AUTO_PROFILER_THREAD_SLEEP;
      result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
                                             MOZ_QS_ALLEVENT, waitFlags);
    }
    NS_WARNING_ASSERTION(result != WAIT_FAILED, "Wait failed");
    if (result == WAIT_TIMEOUT) {
      break;
    }
#if defined(ACCESSIBILITY)
    if (result == WAIT_IO_COMPLETION) {
      if (NS_IsMainThread()) {
        // We executed an APC that would have woken up the hang monitor. Since
        // there are no more APCs pending and we are now going to sleep again,
        // we should notify the hang monitor.
        mozilla::BackgroundHangMonitor().NotifyWait();
      }
      continue;
    }
#endif  // defined(ACCESSIBILITY)

    // Sent messages (via SendMessage and friends) are processed differently
    // than queued messages (via PostMessage); the destination window procedure
    // of the sent message is called during (Get|Peek)Message. Since PeekMessage
    // does not tell us whether it processed any sent messages, we need to query
    // this ahead of time.
    bool haveSentMessagesPending =
        (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;

    MSG msg = {0};
    if (haveSentMessagesPending ||
        ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
      break;
    }
    // The message is intended for another thread that has been synchronized
    // with our input queue; yield to give other threads an opportunity to
    // process the message. This should prevent busy waiting if resumed due
    // to another thread's message.
    ::SwitchToThread();
  }
}

/* static */
HWND WinUtils::GetTopLevelHWND(HWND aWnd, bool aStopIfNotChild,
                               bool aStopIfNotPopup) {
  HWND curWnd = aWnd;
  HWND topWnd = nullptr;

  while (curWnd) {
    topWnd = curWnd;

    if (aStopIfNotChild) {
      DWORD_PTR style = ::GetWindowLongPtrW(curWnd, GWL_STYLE);

      VERIFY_WINDOW_STYLE(style);

      if (!(style & WS_CHILD))  // first top-level window
        break;
    }

    HWND upWnd = ::GetParent(curWnd);  // Parent or owner (if has no parent)

    // GetParent will only return the owner if the passed in window
    // has the WS_POPUP style.
    if (!upWnd && !aStopIfNotPopup) {
      upWnd = ::GetWindow(curWnd, GW_OWNER);
    }
    curWnd = upWnd;
  }

  return topWnd;
}

// Map from native window handles to nsWindow structures. Does not AddRef.
// Inherently unsafe to access outside the main thread.
MOZ_RUNINIT static nsTHashMap<HWND, nsWindow*> sExtantNSWindows;

/* static */
void WinUtils::SetNSWindowPtr(HWND aWnd, nsWindow* aWindow) {
  MOZ_ASSERT(NS_IsMainThread());
  if (!aWindow) {
    sExtantNSWindows.Remove(aWnd);
  } else {
    sExtantNSWindows.InsertOrUpdate(aWnd, aWindow);
  }
}

/* static */
nsWindow* WinUtils::GetNSWindowPtr(HWND aWnd) {
  MOZ_ASSERT(NS_IsMainThread());
  return sExtantNSWindows.Get(aWnd);  // or nullptr
}

/* static */
bool WinUtils::IsOurProcessWindow(HWND aWnd) {
  if (!aWnd) {
    return false;
  }
  DWORD processId = 0;
  ::GetWindowThreadProcessId(aWnd, &processId);
  return (processId == ::GetCurrentProcessId());
}

/* static */
HWND WinUtils::FindOurProcessWindow(HWND aWnd) {
  for (HWND wnd = ::GetParent(aWnd); wnd; wnd = ::GetParent(wnd)) {
    if (IsOurProcessWindow(wnd)) {
      return wnd;
    }
  }
  return nullptr;
}

static bool IsPointInWindow(HWND aWnd, const POINT& aPointInScreen) {
  RECT bounds;
  if (!::GetWindowRect(aWnd, &bounds)) {
    return false;
  }

  return (aPointInScreen.x >= bounds.left && aPointInScreen.x < bounds.right &&
          aPointInScreen.y >= bounds.top && aPointInScreen.y < bounds.bottom);
}

/**
 * FindTopmostWindowAtPoint() returns the topmost child window (topmost means
 * forground in this context) of aWnd.
 */


static HWND FindTopmostWindowAtPoint(HWND aWnd, const POINT& aPointInScreen) {
  if (!::IsWindowVisible(aWnd) || !IsPointInWindow(aWnd, aPointInScreen)) {
    return nullptr;
  }

  HWND childWnd = ::GetTopWindow(aWnd);
  while (childWnd) {
    HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPointInScreen);
    if (topmostWnd) {
      return topmostWnd;
    }
    childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
  }

  return aWnd;
}

struct FindOurWindowAtPointInfo {
  POINT mInPointInScreen;
  HWND mOutWnd;
};

static BOOL CALLBACK FindOurWindowAtPointCallback(HWND aWnd, LPARAM aLPARAM) {
  if (!WinUtils::IsOurProcessWindow(aWnd)) {
    // This isn't one of our top-level windows; continue enumerating.
    return TRUE;
  }

  // Get the top-most child window under the point.  If there's no child
  // window, and the point is within the top-level window, then the top-level
  // window will be returned.  (This is the usual case.  A child window
  // would be returned for plugins.)
  FindOurWindowAtPointInfo* info =
      reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
  HWND childWnd = FindTopmostWindowAtPoint(aWnd, info->mInPointInScreen);
  if (!childWnd) {
    // This window doesn't contain the point; continue enumerating.
    return TRUE;
  }

  // Return the HWND and stop enumerating.
  info->mOutWnd = childWnd;
  return FALSE;
}

/* static */
HWND WinUtils::FindOurWindowAtPoint(const POINT& aPointInScreen) {
  FindOurWindowAtPointInfo info;
  info.mInPointInScreen = aPointInScreen;
  info.mOutWnd = nullptr;

  // This will enumerate all top-level windows in order from top to bottom.
  EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
  return info.mOutWnd;
}

/* static */
UINT WinUtils::GetInternalMessage(UINT aNativeMessage) {
  switch (aNativeMessage) {
    case WM_MOUSEWHEEL:
      return MOZ_WM_MOUSEVWHEEL;
    case WM_MOUSEHWHEEL:
      return MOZ_WM_MOUSEHWHEEL;
    case WM_VSCROLL:
      return MOZ_WM_VSCROLL;
    case WM_HSCROLL:
      return MOZ_WM_HSCROLL;
    default:
      return aNativeMessage;
  }
}

/* static */
UINT WinUtils::GetNativeMessage(UINT aInternalMessage) {
  switch (aInternalMessage) {
    case MOZ_WM_MOUSEVWHEEL:
      return WM_MOUSEWHEEL;
    case MOZ_WM_MOUSEHWHEEL:
      return WM_MOUSEHWHEEL;
    case MOZ_WM_VSCROLL:
      return WM_VSCROLL;
    case MOZ_WM_HSCROLL:
      return WM_HSCROLL;
    default:
      return aInternalMessage;
  }
}

/* static */
uint16_t WinUtils::GetMouseInputSource() {
  int32_t inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE;
  LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
  if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
    inputSource = (lParamExtraInfo & TABLET_INK_TOUCH)
                      ? dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
                      : dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
  }
  return static_cast<uint16_t>(inputSource);
}

/* static */
uint16_t WinUtils::GetMousePointerID() {
  LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
  return lParamExtraInfo & TABLET_INK_ID_MASK;
}

/* static */
bool WinUtils::GetIsMouseFromTouch(EventMessage aEventMessage) {
  const uint32_t MOZ_T_I_SIGNATURE = TABLET_INK_TOUCH | TABLET_INK_SIGNATURE;
  const uint32_t MOZ_T_I_CHECK_TCH = TABLET_INK_TOUCH | TABLET_INK_CHECK;
  return ((aEventMessage == eMouseMove || aEventMessage == eMouseDown ||
           aEventMessage == eMouseUp || aEventMessage == ePointerAuxClick ||
           aEventMessage == eMouseDoubleClick) &&
          (GetMessageExtraInfo() & MOZ_T_I_SIGNATURE) == MOZ_T_I_CHECK_TCH);
}

/* static */
MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) {
  MSG msg;
  msg.message = aMessage;
  msg.wParam = wParam;
  msg.lParam = lParam;
  msg.hwnd = aWnd;
  return msg;
}

#ifdef MOZ_PLACES
/************************************************************************
 * Constructs as AsyncFaviconDataReady Object
 * @param aIOThread : the thread which performs the action
 * @param aURLShortcut : Differentiates between (false)Jumplistcache and
 *                       (true)Shortcutcache
 * @param aRunnable : Executed in the aIOThread when the favicon cache is
 *                    avaiable
 * @param [aPromiseHolder=null]: Optional PromiseHolder that will be forwarded
 *                               to AsyncEncodeAndWriteIcon if getting the
 *                               favicon from the favicon service succeeds. If
 *                               it doesn't succeed, the held MozPromise will
 *                               be rejected.
 ************************************************************************/


AsyncFaviconDataReady::AsyncFaviconDataReady(
    nsIURI* aNewURI, RefPtr<nsISerialEventTarget> aIOThread,
    const bool aURLShortcut, already_AddRefed<nsIRunnable> aRunnable,
    UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
        aPromiseHolder)
    : mNewURI(aNewURI),
      mIOThread(aIOThread),
      mRunnable(aRunnable),
      mPromiseHolder(std::move(aPromiseHolder)),
      mURLShortcut(aURLShortcut) {}

NS_IMETHODIMP
myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
                                       nsIRequest* request, nsresult status,
                                       nsIFile* result) {
  return NS_OK;
}

nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
  if (!mURLShortcut) {
    return NS_OK;
  }

  nsCOMPtr<nsIFile> icoFile;
  nsresult rv =
      FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIURI> mozIconURI;
  rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIChannel> channel;
  rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI,
                     nsContentUtils::GetSystemPrincipal(),
                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
                     nsIContentPolicy::TYPE_INTERNAL_IMAGE);

  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
  nsCOMPtr<nsIStreamListener> listener;
  rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
  NS_ENSURE_SUCCESS(rv, rv);

  return channel->AsyncOpen(listener);
}

NS_IMETHODIMP
AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
                                  const uint8_t* aData,
                                  const nsACString& aMimeType,
                                  uint16_t aWidth) {
  if (!aDataLen || !aData) {
    if (mURLShortcut) {
      OnFaviconDataNotAvailable();
    }

    return NS_OK;
  }

  nsCOMPtr<nsIFile> icoFile;
  nsresult rv =
      FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoString path;
  rv = icoFile->GetPath(path);
  NS_ENSURE_SUCCESS(rv, rv);

  // Decode the image from the format it was returned to us in (probably PNG)
  nsCOMPtr<imgIContainer> container;
  nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
  rv = imgtool->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData),
                                      aDataLen, aMimeType,
                                      getter_AddRefs(container));
  NS_ENSURE_SUCCESS(rv, rv);

  RefPtr<SourceSurface> surface = container->GetFrame(
      imgIContainer::FRAME_FIRST,
      imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);

  RefPtr<DataSourceSurface> dataSurface;
  IntSize size;

  if (mURLShortcut &&
      (surface->GetSize().width < 48 || surface->GetSize().height < 48)) {
    // Create a 48x48 surface and paint the icon into the central rect.
    size.width = std::max(surface->GetSize().width, 48);
    size.height = std::max(surface->GetSize().height, 48);
    dataSurface =
        Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
    NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);

    DataSourceSurface::MappedSurface map;
    if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
      return NS_ERROR_FAILURE;
    }

    RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
        BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
        dataSurface->GetFormat());
    if (!dt) {
      gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
                      "CreateDrawTargetForData";
      return NS_ERROR_OUT_OF_MEMORY;
    }
    dt->FillRect(Rect(0, 0, size.width, size.height),
                 ColorPattern(ToDeviceColor(sRGBColor::OpaqueWhite())));
    IntPoint point;
    point.x = (size.width - surface->GetSize().width) / 2;
    point.y = (size.height - surface->GetSize().height) / 2;
    dt->DrawSurface(surface,
                    Rect(point.x, point.y, surface->GetSize().width,
                         surface->GetSize().height),
                    Rect(Point(0, 0), Size(surface->GetSize().width,
                                           surface->GetSize().height)));

    dataSurface->Unmap();
  } else {
    // By using the input image surface's size, we may end up encoding
    // to a different size than a 16x16 (or bigger for higher DPI) ICO, but
    // Windows will resize appropriately for us. If we want to encode ourselves
    // one day because we like our resizing better, we'd have to manually
    // resize the image here and use GetSystemMetrics w/ SM_CXSMICON and
    // SM_CYSMICON. We don't support resizing images asynchronously at the
    // moment anyway so getting the DPI aware icon size won't help.
    size.width = surface->GetSize().width;
    size.height = surface->GetSize().height;
    dataSurface = surface->GetDataSurface();
    NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
  }

  // Allocate a new buffer that we own and can use out of line in
  // another thread.
  UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface);
  if (!data) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  int32_t stride = 4 * size.width;

  // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
  nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(
      path, std::move(data), stride, size.width, size.height,
      mRunnable.forget(), std::move(mPromiseHolder));
  mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);

  return NS_OK;
}
#endif

// Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed
// in
AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
    const nsAString& aIconPath, UniquePtr<uint8_t[]> aBuffer, uint32_t aStride,
    uint32_t aWidth, uint32_t aHeight, already_AddRefed<nsIRunnable> aRunnable,
    UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
        aPromiseHolder)
    : mIconPath(aIconPath),
      mBuffer(std::move(aBuffer)),
      mRunnable(aRunnable),
      mPromiseHolder(std::move(aPromiseHolder)),
      mStride(aStride),
      mWidth(aWidth),
      mHeight(aHeight) {}

NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
  MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");

  // Note that since we're off the main thread we can't use
  // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
  RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
      mBuffer.get(), mStride, IntSize(mWidth, mHeight),
      SurfaceFormat::B8G8R8A8);

  FILE* file = _wfopen(mIconPath.get(), L"wb");
  if (!file) {
    // Maybe the directory doesn't exist; try creating it, then fopen again.
    nsCOMPtr<nsIFile> comFile;
    MOZ_TRY(NS_NewLocalFile(mIconPath, getter_AddRefs(comFile)));
    nsCOMPtr<nsIFile> dirPath;
    MOZ_TRY(comFile->GetParent(getter_AddRefs(dirPath)));
    nsresult rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
    if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
      return rv;
    }
    file = _wfopen(mIconPath.get(), L"wb");
    if (!file) {
      return NS_ERROR_FAILURE;
    }
  }
  nsresult rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
                                              gfxUtils::eBinaryEncode, file);
  fclose(file);
  NS_ENSURE_SUCCESS(rv, rv);

  if (mRunnable) {
    mRunnable->Run();
  }
  if (mPromiseHolder) {
    mPromiseHolder->ResolveIfExists(mIconPath, __func__);
  }
  return rv;
}

AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {
  if (mPromiseHolder) {
    mPromiseHolder->RejectIfExists(NS_ERROR_FAILURE, __func__);
  }
}

AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
    bool aIgnoreRecent)
    : mIgnoreRecent(aIgnoreRecent) {
  // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
  // threads, as it reads a pref, so cache its value here.
  mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600;

  // Prepare the profile directory cache on the main thread, to ensure we wont
  // do this on non-main threads.
  Unused << NS_GetSpecialDirectory("ProfLDS",
                                   getter_AddRefs(mJumpListCacheDir));
}

NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run() {
  if (!mJumpListCacheDir) {
    return NS_ERROR_FAILURE;
  }
  // Construct the path of our jump list cache
  nsresult rv = mJumpListCacheDir->AppendNative(
      nsDependentCString(FaviconHelper::kJumpListCacheDir));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIDirectoryEnumerator> entries;
  rv = mJumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
  NS_ENSURE_SUCCESS(rv, rv);

  // Loop through each directory entry and remove all ICO files found
  do {
    nsCOMPtr<nsIFile> currFile;
    if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
      break;

    nsAutoString path;
    if (NS_FAILED(currFile->GetPath(path))) continue;

    if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
      // Check if the cached ICO file exists
      bool exists;
      if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;

      if (mIgnoreRecent) {
        // Check to make sure the icon wasn't just recently created.
        // If it was created recently, don't delete it yet.
        int64_t fileModTime = 0;
        rv = currFile->GetLastModifiedTime(&fileModTime);
        fileModTime /= PR_MSEC_PER_SEC;
        // If the icon is older than the regeneration time (+ 10 min to be
        // safe), then it's old and we can get rid of it.
        // This code is only hit directly after a regeneration.
        int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
        if (NS_FAILED(rv) || (nowTime - fileModTime) < mIcoNoDeleteSeconds) {
          continue;
        }
      }

      // We found an ICO file that exists, so we should remove it
      currFile->Remove(false);
    }
  } while (true);

  return NS_OK;
}

AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk() {}

/*
 * (static) If the data is available, will return the path on disk where
 * the favicon for page aFaviconPageURI is stored.  If the favicon does not
 * exist, or its cache is expired, this method will kick off an async request
 * for the icon so that next time the method is called it will be available.
 * @param aFaviconPageURI The URI of the page to obtain
 * @param aICOFilePath The path of the icon file
 * @param aIOThread The thread to perform the Fetch on
 * @param aURLShortcut to distinguish between jumplistcache(false) and
 *                     shortcutcache(true)
 * @param aRunnable Executed in the aIOThread when the favicon cache is
 *                  avaiable
 */

nsresult FaviconHelper::ObtainCachedIconFile(
    nsCOMPtr<nsIURI> aFaviconPageURI, nsString& aICOFilePath,
    RefPtr<LazyIdleThread>& aIOThread, bool aURLShortcut,
    already_AddRefed<nsIRunnable> aRunnable) {
  nsCOMPtr<nsIRunnable> runnable = aRunnable;
  // Obtain the ICO file path
  nsCOMPtr<nsIFile> icoFile;
  nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
  NS_ENSURE_SUCCESS(rv, rv);

  // Check if the cached ICO file already exists
  bool exists;
  rv = icoFile->Exists(&exists);
  NS_ENSURE_SUCCESS(rv, rv);

  if (exists) {
    // Obtain the file's last modification date in seconds
    int64_t fileModTime = 0;
    rv = icoFile->GetLastModifiedTime(&fileModTime);
    fileModTime /= PR_MSEC_PER_SEC;
    int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
    int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);

    // If the last mod call failed or the icon is old then re-cache it
    // This check is in case the favicon of a page changes
    // the next time we try to build the jump list, the data will be available.
    if (NS_FAILED(rv) || (nowTime - fileModTime) > icoReCacheSecondsTimeout) {
      CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
                                       aURLShortcut, runnable.forget(),
                                       nullptr);
      return NS_ERROR_NOT_AVAILABLE;
    }
  } else {
    // The file does not exist yet, obtain it async from the favicon service so
    // that the next time we try to build the jump list it'll be available.
    CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread,
                                     aURLShortcut, runnable.forget(), nullptr);
    return NS_ERROR_NOT_AVAILABLE;
  }

  // The icoFile is filled with a path that exists, get its path
  rv = icoFile->GetPath(aICOFilePath);
  return rv;
}

/**
 * (static)
 * Attempts to obtain a favicon from the nsIFaviconService and cache it on
 * disk in a place where Win32 utilities (like the jump list) can access them.
 *
 * In the event that the favicon was cached recently, the returned MozPromise
 * will resolve with the cache path. If the cache is expired, it will be
 * refreshed before returning the path.
 *
 * In the event that the favicon cannot be retrieved from the nsIFaviconService,
 * or something goes wrong writing the cache to disk, the returned MozPromise
 * will reject with an nsresult code.
 *
 * This is similar to the ObtainCachedIconFile method, except that all IO
 * happens on the aIOThread rather than only some of the IO.
 *
 * @param aFaviconPageURI
 *   The URI of the page to obtain the favicon for.
 * @param aIOThread
 *   The thread to perform the cache check and fetch/write on.
 * @param aCacheDir
 *   Which cache directory to use for the returned favicon (see
 *   FaviconHelper::IconCacheDir).
 * @returns {RefPtr<ObtainCachedIconFileAsyncPromise>}
 *   Resolves with the path of the cached favicon, or rejects with an nsresult
 *   in the event that the favicon cannot be retrieved and/or cached.
 */

auto FaviconHelper::ObtainCachedIconFileAsync(
    nsCOMPtr<nsIURI> aFaviconPageURI, RefPtr<LazyIdleThread>& aIOThread,
    FaviconHelper::IconCacheDir aCacheDir)
    -> RefPtr<ObtainCachedIconFileAsyncPromise> {
  bool useShortcutCacheDir =
      aCacheDir == FaviconHelper::IconCacheDir::ShortcutCacheDir;

  // Obtain the file path that the ICO, if it exists, is expected to be
  // at for aFaviconPageURI
  nsCOMPtr<nsIFile> icoFile;
  nsresult rv =
      GetOutputIconPath(aFaviconPageURI, icoFile, useShortcutCacheDir);
  if (NS_FAILED(rv)) {
    return ObtainCachedIconFileAsyncPromise::CreateAndReject(rv, __func__);
  }

  int32_t icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();

  return InvokeAsync(
      aIOThread, "FaviconHelper::ObtainCachedIconFileAsync disk cache check",
      [icoFile = std::move(icoFile), icoReCacheSecondsTimeout,
       pageURI = std::move(aFaviconPageURI), useShortcutCacheDir]() {
        MOZ_ASSERT(!NS_IsMainThread());
        bool exists;
        nsresult rv = icoFile->Exists(&exists);

        if (NS_FAILED(rv)) {
          return ObtainCachedIconFileAsyncPromise::CreateAndReject(
              rv, "ObtainCachedIconFileAsync disk cache check: exists failed");
        }

        if (exists) {
          // Obtain the file's last modification date in seconds
          int64_t fileModTime = 0;
          rv = icoFile->GetLastModifiedTime(&fileModTime);
          fileModTime /= PR_MSEC_PER_SEC;
          int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);

          // If the file seems to be less than icoReCacheSecondsTimeout old,
          // then we can go ahead and return its path on the filesystem.
          if (NS_SUCCEEDED(rv) &&
              (nowTime - fileModTime) < icoReCacheSecondsTimeout) {
            nsAutoString icoFilePath;
            rv = icoFile->GetPath(icoFilePath);
            if (NS_SUCCEEDED(rv)) {
              return ObtainCachedIconFileAsyncPromise::CreateAndResolve(
                  icoFilePath,
                  "ObtainCachedIconFileAsync disk cache check: found");
            }
          }
        }

        // Now dispatch a runnable to the main thread that will call
        // CacheIconFileFromFaviconURIAsync to request the favicon.
        RefPtr<nsISerialEventTarget> currentThread =
            GetCurrentSerialEventTarget();

        return InvokeAsync(
            GetMainThreadSerialEventTarget(),
            "ObtainCachedIconFileAsync call to "
            "PromiseCacheIconFileFromFaviconURIAsync",
            [useShortcutCacheDir, pageURI = std::move(pageURI),
             icoFile = std::move(icoFile),
             aIOThread = std::move(currentThread)]() {
              auto holder = MakeUnique<
                  MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>();
              RefPtr<ObtainCachedIconFileAsyncPromise> promise =
                  holder->Ensure(__func__);
              CacheIconFileFromFaviconURIAsync(pageURI, icoFile, aIOThread,
                                               useShortcutCacheDir, nullptr,
                                               std::move(holder));
              return promise;
            });
      });
}

// Hash a URI using a cryptographic hash function (currently SHA-256)
// Output will be a base64-encoded string of the hash.
static nsresult HashURI(nsIURI* aUri, nsACString& aUriHash) {
  nsAutoCString spec;
  nsresult rv = aUri->GetSpec(spec);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsICryptoHash> cryptoHash =
      do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = cryptoHash->Init(nsICryptoHash::SHA256);
  NS_ENSURE_SUCCESS(rv, rv);

  // Add some context to the hash to even further reduce the chances of
  // collision. Note that we are hashing this string with its null-terminator.
  const char kHashUriContext[] = "firefox-uri";
  rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(kHashUriContext),
                          sizeof(kHashUriContext));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(spec.BeginReading()),
                          spec.Length());
  NS_ENSURE_SUCCESS(rv, rv);

  rv = cryptoHash->Finish(true, aUriHash);
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

// (static) Obtains the ICO file for the favicon at page aFaviconPageURI
// If successful, the file path on disk is in the format:
// <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
//
// We generate the name with a cryptographically secure hash function in order
// to ensure that malicious websites can't intentionally craft URLs to collide
// with legitimate websites.
nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
                                          nsCOMPtr<nsIFile>& aICOFile,
                                          bool aURLShortcut) {
  nsAutoCString inputURIHash;
  nsresult rv = HashURI(aFaviconPageURI, inputURIHash);
  NS_ENSURE_SUCCESS(rv, rv);
  char* cur = inputURIHash.BeginWriting();
  char* end = inputURIHash.EndWriting();
  for (; cur < end; ++cur) {
    if ('/' == *cur) {
      *cur = '_';
    }
  }

  // Obtain the local profile directory and construct the output icon file path
  rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
  NS_ENSURE_SUCCESS(rv, rv);
  if (!aURLShortcut)
    rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
  else
    rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
  NS_ENSURE_SUCCESS(rv, rv);

  // Append the icon extension
  inputURIHash.AppendLiteral(".ico");
  rv = aICOFile->AppendNative(inputURIHash);

  return rv;
}

// (static) Asynchronously creates a cached ICO file on disk for the favicon of
// page aFaviconPageURI and stores it to disk at the path of aICOFile.
nsresult FaviconHelper::CacheIconFileFromFaviconURIAsync(
    nsCOMPtr<nsIURI> aFaviconPageURI, nsCOMPtr<nsIFile> aICOFile,
    RefPtr<nsISerialEventTarget> aIOThread, bool aURLShortcut,
    already_AddRefed<nsIRunnable> aRunnable,
    UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
        aPromiseHolder) {
  MOZ_ASSERT(NS_IsMainThread());
  nsCOMPtr<nsIRunnable> runnable = aRunnable;
#ifdef MOZ_PLACES
  // Obtain the favicon service and get the favicon for the specified page
  nsCOMPtr<nsIFaviconService> favIconSvc(
      do_GetService("@mozilla.org/browser/favicon-service;1"));
  NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);

  nsCOMPtr<nsIFaviconDataCallback> callback =
      new mozilla::widget::AsyncFaviconDataReady(
          aFaviconPageURI, aIOThread, aURLShortcut, runnable.forget(),
          std::move(aPromiseHolder));

  favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
#endif
  return NS_OK;
}

// Obtains the jump list 'ICO cache timeout in seconds' pref
int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
  // Only obtain the setting at most once from the pref service.
  // In the rare case that 2 threads call this at the same
  // time it is no harm and we will simply obtain the pref twice.
  // None of the taskbar list prefs are currently updated via a
  // pref observer so I think this should suffice.
  const int32_t kSecondsPerDay = 86400;
  static bool alreadyObtained = false;
  static int32_t icoReCacheSecondsTimeout = kSecondsPerDay;
  if (alreadyObtained) {
    return icoReCacheSecondsTimeout;
  }

  // Obtain the pref
  const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
  icoReCacheSecondsTimeout =
      Preferences::GetInt(PREF_ICOTIMEOUT, kSecondsPerDay);
  alreadyObtained = true;
  return icoReCacheSecondsTimeout;
}

/* static */
LayoutDeviceIntRegion WinUtils::ConvertHRGNToRegion(HRGN aRgn) {
  NS_ASSERTION(aRgn, "Don't pass NULL region here");

  LayoutDeviceIntRegion rgn;

  DWORD size = ::GetRegionData(aRgn, 0, nullptr);
  AutoTArray<uint8_t, 100> buffer;
  buffer.SetLength(size);

  RGNDATA* data = reinterpret_cast<RGNDATA*>(buffer.Elements());
  if (!::GetRegionData(aRgn, size, data)) return rgn;

  if (data->rdh.nCount > MAX_RECTS_IN_REGION) {
    rgn = ToIntRect(data->rdh.rcBound);
    return rgn;
  }

  RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
  for (uint32_t i = 0; i < data->rdh.nCount; ++i) {
    RECT* r = rects + i;
    rgn.Or(rgn, ToIntRect(*r));
  }

  return rgn;
}

/* static */
nsAutoRegion WinUtils::RegionToHRGN(const LayoutDeviceIntRegion& aRegion) {
  const uint32_t count = aRegion.GetNumRects();
  const size_t regionBytes = count * sizeof(RECT);
  const size_t regionDataBytes = sizeof(RGNDATAHEADER) + regionBytes;
  // See:
  // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-rgndataheader
  // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-extcreateregion
  auto buffer = MakeUnique<char[]>(regionDataBytes);
  auto* data = reinterpret_cast<RGNDATA*>(buffer.get());
  data->rdh.dwSize = sizeof(RGNDATAHEADER);
  data->rdh.iType = RDH_RECTANGLES;
  data->rdh.nCount = count;
  data->rdh.nRgnSize = regionBytes;
  data->rdh.rcBound = ToWinRect(aRegion.GetBounds());
  RECT* buf = (RECT*)data->Buffer;
  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
    *buf++ = ToWinRect(iter.Get());
  }
  return nsAutoRegion(::ExtCreateRegion(nullptr, regionDataBytes, data));
}

LayoutDeviceIntRect WinUtils::ToIntRect(const RECT& aRect) {
  return LayoutDeviceIntRect(aRect.left, aRect.top, aRect.right - aRect.left,
                             aRect.bottom - aRect.top);
}

RECT WinUtils::ToWinRect(const LayoutDeviceIntRect& aRect) {
  return {
      .left = aRect.x,
      .top = aRect.y,
      .right = aRect.XMost(),
      .bottom = aRect.YMost(),
  };
}

/* static */
bool WinUtils::IsIMEEnabled(const InputContext& aInputContext) {
  return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
}

/* static */
bool WinUtils::IsIMEEnabled(IMEEnabled aIMEState) {
  return aIMEState == IMEEnabled::Enabled;
}

/* static */
void WinUtils::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray,
                                         uint32_t aModifiers, UINT aMessage) {
  MOZ_ASSERT(!(aModifiers & nsIWidget::ALTGRAPH) ||
             !(aModifiers & (nsIWidget::CTRL_L | nsIWidget::ALT_R)));
  if (aMessage == WM_KEYUP) {
    // If AltGr is released, ControlLeft key is released first, then,
    // AltRight key is released.
    if (aModifiers & nsIWidget::ALTGRAPH) {
      aArray->AppendElement(
          KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
      aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
    }
    for (uint32_t i = std::size(sModifierKeyMap); i; --i) {
      const uint32_t* map = sModifierKeyMap[i - 1];
      if (aModifiers & map[0]) {
        aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
      }
    }
  } else {
    for (uint32_t i = 0; i < std::size(sModifierKeyMap); ++i) {
      const uint32_t* map = sModifierKeyMap[i];
      if (aModifiers & map[0]) {
        aArray->AppendElement(KeyPair(map[1], map[2], map[3]));
      }
    }
    // If AltGr is pressed, ControlLeft key is pressed first, then,
    // AltRight key is pressed.
    if (aModifiers & nsIWidget::ALTGRAPH) {
      aArray->AppendElement(
          KeyPair(VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft));
      aArray->AppendElement(KeyPair(VK_MENU, VK_RMENU, ScanCode::eAltRight));
    }
  }
}

/* static */
nsresult WinUtils::WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
  RefPtr<SourceSurface> surface = aImage->GetFrame(
      imgIContainer::FRAME_FIRST,
      imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);

  return WriteBitmap(aFile, surface);
}

/* static */
nsresult WinUtils::WriteBitmap(nsIFile* aFile, SourceSurface* surface) {
  nsresult rv;

  // For either of the following formats we want to set the biBitCount member
  // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
  // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
  // for the BI_RGB value we use for the biCompression member.
  MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
             surface->GetFormat() == SurfaceFormat::B8G8R8X8);

  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);

  int32_t width = dataSurface->GetSize().width;
  int32_t height = dataSurface->GetSize().height;
  int32_t bytesPerPixel = 4 * sizeof(uint8_t);
  uint32_t bytesPerRow = bytesPerPixel * width;
  bool hasAlpha = surface->GetFormat() == SurfaceFormat::B8G8R8A8;

  // initialize these bitmap structs which we will later
  // serialize directly to the head of the bitmap file
  BITMAPV4HEADER bmi;
  memset(&bmi, 0, sizeof(BITMAPV4HEADER));
  bmi.bV4Size = sizeof(BITMAPV4HEADER);
  bmi.bV4Width = width;
  bmi.bV4Height = height;
  bmi.bV4Planes = 1;
  bmi.bV4BitCount = (WORD)bytesPerPixel * 8;
  bmi.bV4V4Compression = hasAlpha ? BI_BITFIELDS : BI_RGB;
  bmi.bV4SizeImage = bytesPerRow * height;
  bmi.bV4CSType = LCS_sRGB;
  if (hasAlpha) {
    bmi.bV4RedMask = 0x00FF0000;
    bmi.bV4GreenMask = 0x0000FF00;
    bmi.bV4BlueMask = 0x000000FF;
    bmi.bV4AlphaMask = 0xFF000000;
  }

  BITMAPFILEHEADER bf;
  DWORD colormask[3];
  bf.bfType = 0x4D42;  // 'BM'
  bf.bfReserved1 = 0;
  bf.bfReserved2 = 0;
  bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) +
                 (hasAlpha ? sizeof(colormask) : 0);
  bf.bfSize = bf.bfOffBits + bmi.bV4SizeImage;

  // get a file output stream
  nsCOMPtr<nsIOutputStream> stream;
  rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
  NS_ENSURE_SUCCESS(rv, rv);

  DataSourceSurface::MappedSurface map;
  if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
    return NS_ERROR_FAILURE;
  }

  // write the bitmap headers and rgb pixel data to the file
  rv = NS_ERROR_FAILURE;
  if (stream) {
    uint32_t written;
    stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
    if (written == sizeof(BITMAPFILEHEADER)) {
      stream->Write((const char*)&bmi, sizeof(BITMAPV4HEADER), &written);
      if (written == sizeof(BITMAPV4HEADER)) {
        if (hasAlpha) {
          // color mask
          colormask[0] = 0x00FF0000;
          colormask[1] = 0x0000FF00;
          colormask[2] = 0x000000FF;

          stream->Write((const char*)colormask, sizeof(colormask), &written);
        }
        if (!hasAlpha || written == sizeof(colormask)) {
          // write out the image data backwards because the desktop won't
          // show bitmaps with negative heights for top-to-bottom
          uint32_t i = map.mStride * height;
          do {
            i -= map.mStride;
            stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
            if (written == bytesPerRow) {
              rv = NS_OK;
            } else {
              rv = NS_ERROR_FAILURE;
              break;
            }
          } while (i != 0);
        }
      }
    }

    stream->Close();
  }

  dataSurface->Unmap();

  return rv;
}

// This is in use here and in dom/events/TouchEvent.cpp
/* static */
uint32_t WinUtils::IsTouchDeviceSupportPresent() {
  int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
  int32_t touchFlags = NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH;
  if (StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled()) {
    touchFlags |= NID_EXTERNAL_PEN | NID_INTEGRATED_PEN;
  }
  return (touchCapabilities & NID_READY) && (touchCapabilities & touchFlags);
}

/* static */
uint32_t WinUtils::GetMaxTouchPoints() {
  if (IsTouchDeviceSupportPresent()) {
    return GetSystemMetrics(SM_MAXIMUMTOUCHES);
  }
  return 0;
}

/* static */
POWER_PLATFORM_ROLE
WinUtils::GetPowerPlatformRole() {
  typedef POWER_PLATFORM_ROLE(WINAPI *
                              PowerDeterminePlatformRoleEx)(ULONG Version);
  static PowerDeterminePlatformRoleEx power_determine_platform_role =
      reinterpret_cast<PowerDeterminePlatformRoleEx>(::GetProcAddress(
          ::LoadLibraryW(L"PowrProf.dll"), "PowerDeterminePlatformRoleEx"));

  POWER_PLATFORM_ROLE powerPlatformRole = PlatformRoleUnspecified;
  if (!power_determine_platform_role) {
    return powerPlatformRole;
  }

  return power_determine_platform_role(POWER_PLATFORM_ROLE_V2);
}

// static
bool WinUtils::GetAutoRotationState(AR_STATE* aRotationState) {
  typedef BOOL(WINAPI * GetAutoRotationStateFunc)(PAR_STATE pState);
  static GetAutoRotationStateFunc get_auto_rotation_state_func =
      reinterpret_cast<GetAutoRotationStateFunc>(::GetProcAddress(
          GetModuleHandleW(L"user32.dll"), "GetAutoRotationState"));
  if (get_auto_rotation_state_func) {
    ZeroMemory(aRotationState, sizeof(AR_STATE));
    return get_auto_rotation_state_func(aRotationState);
  }
  return false;
}

// static
void WinUtils::GetClipboardFormatAsString(UINT aFormat, nsAString& aOutput) {
  wchar_t buf[256] = {};
  // Get registered format name and ensure the existence of a terminating '\0'
  // if the registered name is more than 256 characters.
  if (::GetClipboardFormatNameW(aFormat, buf, ARRAYSIZE(buf) - 1)) {
    aOutput.Append(buf);
    return;
  }
  // Standard clipboard formats
  // https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
  switch (aFormat) {
    case CF_TEXT:  // 1
      aOutput.Append(u"CF_TEXT"_ns);
      break;
    case CF_BITMAP:  // 2
      aOutput.Append(u"CF_BITMAP"_ns);
      break;
    case CF_DIB:  // 8
      aOutput.Append(u"CF_DIB"_ns);
      break;
    case CF_UNICODETEXT:  // 13
      aOutput.Append(u"CF_UNICODETEXT"_ns);
      break;
    case CF_HDROP:  // 15
      aOutput.Append(u"CF_HDROP"_ns);
      break;
    case CF_DIBV5:  // 17
      aOutput.Append(u"CF_DIBV5"_ns);
      break;
    default:
      aOutput.AppendPrintf("%u", aFormat);
      break;
  }
}

static bool IsTabletDevice() {
  // Guarantees that:
  // - The device has a touch screen.
  // - It is used as a tablet which means that it has no keyboard connected.

  if (WindowsUIUtils::GetInWin10TabletMode()) {
    return true;
  }

  if (!GetSystemMetrics(SM_MAXIMUMTOUCHES)) {
    return false;
  }

  // If the device is docked, the user is treating the device as a PC.
  if (GetSystemMetrics(SM_SYSTEMDOCKED)) {
    return false;
  }

  // If the device is not supporting rotation, it's unlikely to be a tablet,
  // a convertible or a detachable. See:
  // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
  AR_STATE rotation_state;
  if (WinUtils::GetAutoRotationState(&rotation_state) &&
      (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR))) {
    return false;
  }

  // PlatformRoleSlate was added in Windows 8+.
  POWER_PLATFORM_ROLE role = WinUtils::GetPowerPlatformRole();
  if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
    return !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
  }
  return false;
}

bool WinUtils::SystemHasMouse() {
  // As per MSDN, this value is rarely false because of virtual mice, and
  // some machines report the existance of a mouse port as a mouse.
  //
  // We probably could try to distinguish if we wanted, but a virtual mouse
  // might be there for a reason, and maybe we shouldn't assume we know
  // better.
  return !!::GetSystemMetrics(SM_MOUSEPRESENT);
}

/* static */
PointerCapabilities WinUtils::GetPrimaryPointerCapabilities() {
  if (IsTabletDevice()) {
    return PointerCapabilities::Coarse;
  }

  if (SystemHasMouse()) {
    return PointerCapabilities::Fine | PointerCapabilities::Hover;
  }

  if (IsTouchDeviceSupportPresent()) {
    return PointerCapabilities::Coarse;
  }

  return PointerCapabilities::None;
}

bool WinUtils::SystemHasTouch() {
  int digitizerMetrics = ::GetSystemMetrics(SM_DIGITIZER);
  return (digitizerMetrics & NID_INTEGRATED_TOUCH) ||
         (digitizerMetrics & NID_EXTERNAL_TOUCH);
}

bool WinUtils::SystemHasPen() {
  int digitizerMetrics = ::GetSystemMetrics(SM_DIGITIZER);
  return (digitizerMetrics & NID_INTEGRATED_PEN) ||
         (digitizerMetrics & NID_EXTERNAL_PEN);
}

/* static */
PointerCapabilities WinUtils::GetAllPointerCapabilities() {
  PointerCapabilities pointerCapabilities = PointerCapabilities::None;

  if (SystemHasTouch()) {
    pointerCapabilities |= PointerCapabilities::Coarse;
  }

  if (SystemHasPen() || SystemHasMouse()) {
    pointerCapabilities |=
        PointerCapabilities::Fine | PointerCapabilities::Hover;
  }

  return pointerCapabilities;
}

/* static */
bool WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath) {
  static mozilla::LazyLogModule sNTFSLog("NTFS");

  MOZ_LOG(
      sNTFSLog, LogLevel::Debug,
      ("ResolveJunctionPointsAndSymLinks: Resolving path: %S", aPath.c_str()));

  wchar_t path[MAX_PATH] = {0};

  nsAutoHandle handle(::CreateFileW(
      aPath.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));

  if (handle == INVALID_HANDLE_VALUE) {
    MOZ_LOG(sNTFSLog, LogLevel::Error,
            ("Failed to open file handle to resolve path. GetLastError=%lu",
             GetLastError()));
    return false;
  }

  DWORD pathLen = GetFinalPathNameByHandleW(
      handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
  if (pathLen == 0 || pathLen >= MAX_PATH) {
    MOZ_LOG(
        sNTFSLog, LogLevel::Error,
        ("GetFinalPathNameByHandleW failed. GetLastError=%lu", GetLastError()));
    return false;
  }
  aPath = path;

  // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
  // but that confuses some APIs so strip it off. It will also put
  // '\\?\UNC\' in front of network paths, we convert that to '\\'.
  if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
    aPath.erase(2, 6);
  } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
    aPath.erase(0, 4);
  }

  MOZ_LOG(sNTFSLog, LogLevel::Debug,
          ("ResolveJunctionPointsAndSymLinks: Resolved path to: %S",
           aPath.c_str()));
  return true;
}

/* static */
bool WinUtils::ResolveJunctionPointsAndSymLinks(nsIFile* aPath) {
  MOZ_ASSERT(aPath);

  nsAutoString filePath;
  nsresult rv = aPath->GetPath(filePath);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  std::wstring resolvedPath(filePath.get());
  if (!ResolveJunctionPointsAndSymLinks(resolvedPath)) {
    return false;
  }

  rv = aPath->InitWithPath(nsDependentString(resolvedPath.c_str()));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  return true;
}

/* static */
bool WinUtils::RunningFromANetworkDrive() {
  wchar_t exePath[MAX_PATH];
  if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) {
    return false;
  }

  std::wstring exeString(exePath);
  if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString)) {
    return false;
  }

  wchar_t volPath[MAX_PATH];
  if (!::GetVolumePathNameW(exeString.c_str(), volPath, MAX_PATH)) {
    return false;
  }

  return (::GetDriveTypeW(volPath) == DRIVE_REMOTE);
}

/* static */
bool WinUtils::CanonicalizePath(nsAString& aPath) {
  wchar_t tempPath[MAX_PATH + 1];
  if (!PathCanonicalizeW(tempPath,
                         (char16ptr_t)PromiseFlatString(aPath).get())) {
    return false;
  }
  aPath = tempPath;
  MOZ_ASSERT(aPath.Length() <= MAX_PATH);
  return true;
}

/* static */
bool WinUtils::MakeLongPath(nsAString& aPath) {
  wchar_t tempPath[MAX_PATH + 1];
  DWORD longResult =
      GetLongPathNameW((char16ptr_t)PromiseFlatString(aPath).get(), tempPath,
                       std::size(tempPath));
  if (longResult > std::size(tempPath)) {
    // Our buffer is too short, and we're guaranteeing <= MAX_PATH results.
    return false;
  } else if (longResult) {
    // Success.
    aPath = tempPath;
    MOZ_ASSERT(aPath.Length() <= MAX_PATH);
  }
  // GetLongPathNameW returns 0 if the path is not found or is not rooted,
  // but we shouldn't consider that a failure condition.
  return true;
}

/* static */
bool WinUtils::UnexpandEnvVars(nsAString& aPath) {
  wchar_t tempPath[MAX_PATH + 1];
  // PathUnExpandEnvStringsW returns false if it doesn't make any
  // substitutions. Silently continue using the unaltered path.
  if (PathUnExpandEnvStringsW((char16ptr_t)PromiseFlatString(aPath).get(),
                              tempPath, std::size(tempPath))) {
    aPath = tempPath;
    MOZ_ASSERT(aPath.Length() <= MAX_PATH);
  }
  return true;
}

/* static */
WinUtils::WhitelistVec WinUtils::BuildWhitelist() {
  WhitelistVec result;

  Unused << result.emplaceBack(
      std::make_pair(nsString(u"%ProgramFiles%"_ns), nsDependentString()));

  // When no substitution is required, set the void flag
  result.back().second.SetIsVoid(true);

  Unused << result.emplaceBack(
      std::make_pair(nsString(u"%SystemRoot%"_ns), nsDependentString()));
  result.back().second.SetIsVoid(true);

  wchar_t tmpPath[MAX_PATH + 1] = {};
  if (GetTempPath(MAX_PATH, tmpPath)) {
    // GetTempPath's result always ends with a backslash, which we don't want
    uint32_t tmpPathLen = wcslen(tmpPath);
    if (tmpPathLen) {
      tmpPath[tmpPathLen - 1] = 0;
    }

    nsAutoString cleanTmpPath(tmpPath);
    if (UnexpandEnvVars(cleanTmpPath)) {
      constexpr auto tempVar = u"%TEMP%"_ns;
      Unused << result.emplaceBack(std::make_pair(
          nsString(cleanTmpPath), nsDependentString(tempVar, 0)));
    }
  }

  // If we add more items to the whitelist, ensure we still don't invoke an
  // unnecessary heap allocation.
  MOZ_ASSERT(result.length() <= kMaxWhitelistedItems);

  return result;
}

/**
 * This function provides an array of (system path, substitution) pairs that are
 * considered to be acceptable with respect to privacy, for the purposes of
 * submitting within telemetry or crash reports.
 *
 * The substitution string's void flag may be set. If it is, no subsitution is
 * necessary. Otherwise, the consumer should replace the system path with the
 * substitution.
 *
 * @see PreparePathForTelemetry for an example of its usage.
 */

/* static */
const WinUtils::WhitelistVec& WinUtils::GetWhitelistedPaths() {
  static WhitelistVec sWhitelist([]() -> WhitelistVec {
    auto setClearFn = [ptr = &sWhitelist]() -> void {
      RunOnShutdown([ptr]() -> void { ptr->clear(); },
                    ShutdownPhase::XPCOMShutdownFinal);
    };

    if (NS_IsMainThread()) {
      setClearFn();
    } else {
      SchedulerGroup::Dispatch(NS_NewRunnableFunction(
          "WinUtils::GetWhitelistedPaths", std::move(setClearFn)));
    }

    return BuildWhitelist();
  }());
  return sWhitelist;
}

/**
 * This function is located here (as opposed to nsSystemInfo or elsewhere)
 * because we need to gather this information as early as possible during
 * startup.
 */

/* static */
bool WinUtils::GetAppInitDLLs(nsAString& aOutput) {
  aOutput.Truncate();
  HKEY hkey = NULL;
  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
                    0, KEY_QUERY_VALUE, &hkey)) {
    return false;
  }
  nsAutoRegKey key(hkey);
  LONG status;
  const wchar_t kLoadAppInitDLLs[] = L"LoadAppInit_DLLs";
  DWORD loadAppInitDLLs = 0;
  DWORD loadAppInitDLLsLen = sizeof(loadAppInitDLLs);
  status = RegQueryValueExW(hkey, kLoadAppInitDLLs, nullptr, nullptr,
                            (LPBYTE)&loadAppInitDLLs, &loadAppInitDLLsLen);
  if (status != ERROR_SUCCESS) {
    return false;
  }
  if (!loadAppInitDLLs) {
    // If loadAppInitDLLs is zero then AppInit_DLLs is disabled.
    // In this case we'll return true along with an empty output string.
    return true;
  }
  DWORD numBytes = 0;
  const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
  // Query for required buffer size
  status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr, nullptr,
                            &numBytes);
  if (status != ERROR_SUCCESS) {
    return false;
  }
  // Allocate the buffer and query for the actual data
  mozilla::UniquePtr<wchar_t[]> data =
      mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
  status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr, nullptr,
                            (LPBYTE)data.get(), &numBytes);
  if (status != ERROR_SUCCESS) {
    return false;
  }
  // For each token, split up the filename components and then check the
  // name of the file.
  const wchar_t kDelimiters[] = L", ";
  wchar_t* tokenContext = nullptr;
  wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
  while (token) {
    nsAutoString cleanPath(token);
    // Since these paths are short paths originating from the registry, we need
    // to canonicalize them, lengthen them, and sanitize them before we can
    // check them against the whitelist
    if (PreparePathForTelemetry(cleanPath)) {
--> --------------------

--> maximum size reached

--> --------------------

96%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.78Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.