/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

#ifndef WIDGET_WINDOWS_NSWINDOW_H_
#define WIDGET_WINDOWS_NSWINDOW_H_

/*
 * nsWindow - Native window management and event handling.
 */

#include "mozilla/RefPtr.h"
#include "nsBaseWidget.h"
#include "CompositorWidget.h"
#include "mozilla/EventForwards.h"
#include "nsClassHashtable.h"
#include <windows.h>
#include "touchinjection_sdk80.h"
#include "nsdefs.h"
#include "nsUserIdleService.h"
#include "nsToolkit.h"
#include "nsString.h"
#include "nsTArray.h"
#include "gfxWindowsPlatform.h"
#include "gfxWindowsSurface.h"
#include "nsWindowDbg.h"
#include "cairo.h"
#include "nsRegion.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/Maybe.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/DataMutex.h"
#include "mozilla/UniquePtr.h"
#include "nsMargin.h"
#include "nsRegionFwd.h"

#include "nsWinGesture.h"
#include "WinPointerEvents.h"
#include "WinUtils.h"
#include "WindowHook.h"
#include "TaskbarWindowPreview.h"

#ifdef ACCESSIBILITY
#  include "oleacc.h"
#  include "mozilla/a11y/LocalAccessible.h"
#endif

#include "nsUXThemeData.h"
#include "nsIUserIdleServiceInternal.h"

#include "IMMHandler.h"
#include "CheckInvariantWrapper.h"

/**
 * Forward class definitions
 */

class nsNativeDragTarget;
class nsIRollupListener;
class imgIContainer;

namespace mozilla {
class WidgetMouseEvent;
namespace widget {
class NativeKey;
class InProcessWinCompositorWidget;
struct MSGResult;
class DirectManipulationOwner;
}  // namespace widget
}  // namespace mozilla

/**
 * Forward Windows-internal definitions of otherwise incomplete ones provided by
 * the SDK.
 */
const CLSID CLSID_ImmersiveShell = {
    0xC2F03A33,
    0x21F5,
    0x47FA,
    {0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39}};

// Virtual Desktop.

EXTERN_C const IID IID_IVirtualDesktopManager;
MIDL_INTERFACE("a5cd92ff-29be-454c-8d04-d82879fb3f1b")
IVirtualDesktopManager : public IUnknown {
 public:
  virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop(
      __RPC__in HWND topLevelWindow, __RPC__out BOOL * onCurrentDesktop) = 0;
  virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId(
      __RPC__in HWND topLevelWindow, __RPC__out GUID * desktopId) = 0;
  virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop(
      __RPC__in HWND topLevelWindow, __RPC__in REFGUID desktopId) = 0;
};

#ifdef __MINGW32__
__CRT_UUID_DECL(IVirtualDesktopManager, 0xa5cd92ff, 0x29be, 0x454c, 0x8d, 0x04,
                0xd8, 0x28, 0x79, 0xfb, 0x3f, 0x1b)
#endif

/**
 * Native WIN32 window wrapper.
 */

class nsWindow final : public nsBaseWidget {
 public:
  using WindowHook = mozilla::widget::WindowHook;
  using IMEContext = mozilla::widget::IMEContext;
  using WidgetEventTime = mozilla::WidgetEventTime;

  NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsBaseWidget)

  explicit nsWindow(bool aIsChildWindow = false);

  void SendAnAPZEvent(mozilla::InputData& aEvent);

  /*
   * Init a standard gecko event for this widget.
   * @param aEvent the event to initialize.
   * @param aPoint message position in physical coordinates.
   */
  void InitEvent(mozilla::WidgetGUIEvent& aEvent,
                 LayoutDeviceIntPoint* aPoint = nullptr);

  /*
   * Returns WidgetEventTime instance which is initialized with current message
   * time.
   */
  WidgetEventTime CurrentMessageWidgetEventTime() const;

  /*
   * Dispatch a gecko keyboard event for this widget. This
   * is called by KeyboardLayout to dispatch gecko events.
   * Returns true if it's consumed.  Otherwise, false.
   */
  bool DispatchKeyboardEvent(mozilla::WidgetKeyboardEvent* aEvent);

  /*
   * Dispatch a gecko wheel event for this widget. This
   * is called by ScrollHandler to dispatch gecko events.
   * Returns true if it's consumed.  Otherwise, false.
   */
  bool DispatchWheelEvent(mozilla::WidgetWheelEvent* aEvent);

  /*
   * Dispatch a gecko content command event for this widget. This
   * is called by ScrollHandler to dispatch gecko events.
   * Returns true if it's consumed.  Otherwise, false.
   */
  bool DispatchContentCommandEvent(mozilla::WidgetContentCommandEvent* aEvent);

  /*
   * Return the parent window, if it exists.
   */
  nsWindow* GetParentWindowBase(bool aIncludeOwner);

  /*
   * Return true if this is a top level widget.
   */
  bool IsTopLevelWidget() { return mIsTopWidgetWindow; }

  // nsIWidget interface
  using nsBaseWidget::Create;  // for Create signature not overridden here
  [[nodiscard]] nsresult Create(nsIWidget* aParent,
                                nsNativeWidget aNativeParent,
                                const LayoutDeviceIntRect& aRect,
                                InitData* aInitData = nullptr) override;
  void Destroy() override;
  void SetParent(nsIWidget* aNewParent) override;
  nsIWidget* GetParent(void) override;
  float GetDPI() override;
  double GetDefaultScaleInternal() override;
  int32_t LogToPhys(double aValue);
  mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override {
    if (mozilla::widget::WinUtils::IsPerMonitorDPIAware()) {
      return mozilla::DesktopToLayoutDeviceScale(1.0);
    } else {
      return mozilla::DesktopToLayoutDeviceScale(GetDefaultScaleInternal());
    }
  }

  void Show(bool aState) override;
  bool IsVisible() const override;
  void ConstrainPosition(DesktopIntPoint&) override;
  void SetSizeConstraints(const SizeConstraints& aConstraints) override;
  void LockAspectRatio(bool aShouldLock) override;
  const SizeConstraints GetSizeConstraints() override;
  void SetInputRegion(const InputRegion&) override;
  void Move(double aX, double aY) override;
  void Resize(double aWidth, double aHeight, bool aRepaint) override;
  void Resize(double aX, double aY, double aWidth, double aHeight,
              bool aRepaint) override;
  mozilla::Maybe<bool> IsResizingNativeWidget() override;
  void PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget* aWidget,
                   bool aActivate) override;
  void SetSizeMode(nsSizeMode aMode) override;
  nsSizeMode SizeMode() override;
  void GetWorkspaceID(nsAString& workspaceID) override;
  void MoveToWorkspace(const nsAString& workspaceID) override;
  void SuppressAnimation(bool aSuppress) override;
  void Enable(bool aState) override;
  bool IsEnabled() const override;
  void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override;
  LayoutDeviceIntRect GetBounds() override;
  LayoutDeviceIntRect GetScreenBounds() override;
  [[nodiscard]] nsresult GetRestoredBounds(LayoutDeviceIntRect& aRect) override;
  LayoutDeviceIntRect GetClientBounds() override;
  LayoutDeviceIntPoint GetClientOffset() override;
  void SetBackgroundColor(const nscolor& aColor) override;
  void SetCursor(const Cursor&) override;
  bool PrepareForFullscreenTransition(nsISupports** aData) override;
  void PerformFullscreenTransition(FullscreenTransitionStage aStage,
                                   uint16_t aDuration, nsISupports* aData,
                                   nsIRunnable* aCallback) override;
  void CleanupFullscreenTransition() override;
  nsresult MakeFullScreen(bool aFullScreen) override;
  void HideWindowChrome(bool aShouldHide) override;
  void Invalidate(bool aEraseBackground = false, bool aUpdateNCArea = false,
                  bool aIncludeChildren = false);
  void Invalidate(const LayoutDeviceIntRect& aRect) override;
  void* GetNativeData(uint32_t aDataType) override;
  void FreeNativeData(void* data, uint32_t aDataType) override;
  nsresult SetTitle(const nsAString& aTitle) override;
  void SetIcon(const nsAString& aIconSpec) override;
  LayoutDeviceIntPoint WidgetToScreenOffset() override;
  LayoutDeviceIntMargin ClientToWindowMargin() override;
  nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus& aStatus) override;
  void EnableDragDrop(bool aEnable) override;
  void CaptureMouse(bool aCapture);
  void CaptureRollupEvents(bool aDoCapture) override;
  [[nodiscard]] nsresult GetAttention(int32_t aCycleCount) override;
  bool HasPendingInputEvent() override;
  WindowRenderer* GetWindowRenderer() override;
  void SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) override;
  [[nodiscard]] nsresult OnDefaultButtonLoaded(
      const LayoutDeviceIntRect& aButtonRect) override;
  nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                    int32_t aNativeKeyCode,
                                    uint32_t aModifierFlags,
                                    const nsAString& aCharacters,
                                    const nsAString& aUnmodifiedCharacters,
                                    nsIObserver* aObserver) override;
  nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                      NativeMouseMessage aNativeMessage,
                                      mozilla::MouseButton aButton,
                                      nsIWidget::Modifiers aModifierFlags,
                                      nsIObserver* aObserver) override;

  nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
                                     nsIObserver* aObserver) override {
    return SynthesizeNativeMouseEvent(
        aPoint, NativeMouseMessage::Move, mozilla::MouseButton::eNotPressed,
        nsIWidget::Modifiers::NO_MODIFIERS, aObserver);
  }

  nsresult SynthesizeNativeMouseScrollEvent(
      LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX,
      double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
      uint32_t aAdditionalFlags, nsIObserver* aObserver) override;

  nsresult SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase,
                                       LayoutDeviceIntPoint aPoint,
                                       double aDeltaX, double aDeltaY,
                                       int32_t aModifierFlagsn,
                                       nsIObserver* aObserver) override;

  void SetInputContext(const InputContext& aContext,
                       const InputContextAction& aAction) override;
  InputContext GetInputContext() override;
  TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override;
  void SetTransparencyMode(TransparencyMode aMode) override;
  TransparencyMode GetTransparencyMode() override;
  void UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) override;
  nsresult SetNonClientMargins(const LayoutDeviceIntMargin&) override;
  void SetResizeMargin(mozilla::LayoutDeviceIntCoord aResizeMargin) override;
  void SetDrawsInTitlebar(bool aState) override;
  void UpdateWindowDraggingRegion(
      const LayoutDeviceIntRegion& aRegion) override;

  void UpdateThemeGeometries(
      const nsTArray<ThemeGeometry>& aThemeGeometries) override;
  uint32_t GetMaxTouchPoints() const override;
  void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass,
                      const nsAString& xulWinName) override;

  /**
   * Event helpers
   */
  bool DispatchMouseEvent(mozilla::EventMessage aEventMessage, WPARAM wParam,
                          LPARAM lParam, bool aIsContextMenuKey,
                          int16_t aButton, uint16_t aInputSource,
                          WinPointerInfo* aPointerInfo = nullptr,
                          bool aIgnoreAPZ = false);
  void DispatchPendingEvents();
  void DispatchCustomEvent(const nsString& eventName);

#ifdef ACCESSIBILITY
  /**
   * Return an accessible associated with the window.
   */
  mozilla::a11y::LocalAccessible* GetAccessible();
#endif  // ACCESSIBILITY

  /**
   * Window utilities
   */
  nsWindow* GetTopLevelWindow(bool aStopOnDialogOrPopup);
  WNDPROC GetPrevWindowProc() { return mPrevWndProc.valueOr(nullptr); }
  WindowHook& GetWindowHook() { return mWindowHook; }
  nsWindow* GetParentWindow(bool aIncludeOwner);

  /**
   * Misc.
   */
  bool WidgetTypeSupportsAcceleration() override;

  void ForcePresent();
  bool TouchEventShouldStartDrag(mozilla::EventMessage aEventMessage,
                                 LayoutDeviceIntPoint aEventPoint);

  void SetSmallIcon(HICON aIcon);
  void SetBigIcon(HICON aIcon);
  void SetSmallIconNoData();
  void SetBigIconNoData();

  static void SetIsRestoringSession(const bool aIsRestoringSession) {
    sIsRestoringSession = aIsRestoringSession;
  }

  bool IsRTL() const { return mIsRTL; }

  /**
   * AssociateDefaultIMC() associates or disassociates the default IMC for
   * the window.
   *
   * @param aAssociate    TRUE, associates the default IMC with the window.
   *                      Otherwise, disassociates the default IMC from the
   *                      window.
   * @return              TRUE if this method associated the default IMC with
   *                      disassociated window or disassociated the default IMC
   *                      from associated window.
   *                      Otherwise, i.e., if this method did nothing actually,
   *                      FALSE.
   */
  bool AssociateDefaultIMC(bool aAssociate);

  bool HasTaskbarIconBeenCreated() { return mHasTaskbarIconBeenCreated; }
  // Called when either the nsWindow or an nsITaskbarTabPreview receives the
  // noticiation that this window has its icon placed on the taskbar.
  void SetHasTaskbarIconBeenCreated(bool created = true) {
    mHasTaskbarIconBeenCreated = created;
  }

  // Getter/setter for the nsITaskbarWindowPreview for this nsWindow
  already_AddRefed<nsITaskbarWindowPreview> GetTaskbarPreview() {
    nsCOMPtr<nsITaskbarWindowPreview> preview(
        do_QueryReferent(mTaskbarPreview));
    return preview.forget();
  }
  void SetTaskbarPreview(nsITaskbarWindowPreview* preview) {
    mTaskbarPreview = do_GetWeakReference(preview);
  }

  void ReparentNativeWidget(nsIWidget* aNewParent) override;

  // Open file picker tracking
  void PickerOpen();
  void PickerClosed();

  bool DestroyCalled() { return mDestroyCalled; }

  bool IsPopup();
  bool ShouldUseOffMainThreadCompositing() override;

  const IMEContext& DefaultIMC() const { return mDefaultIMC; }

  void GetCompositorWidgetInitData(
      mozilla::widget::CompositorWidgetInitData* aInitData) override;
  bool IsTouchWindow() const { return mTouchWindow; }
  bool SynchronouslyRepaintOnResize() override;
  void MaybeDispatchInitialFocusEvent() override;

  void LocalesChanged() override;

  void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override;
  void MaybeEnableWindowOcclusion(bool aEnable);

  /*
   * Return the HWND or null for this widget.
   */
  HWND GetWindowHandle() {
    return static_cast<HWND>(GetNativeData(NS_NATIVE_WINDOW));
  }

  /*
   * Touch input injection apis
   */
  nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                      TouchPointerState aPointerState,
                                      LayoutDeviceIntPoint aPoint,
                                      double aPointerPressure,
                                      uint32_t aPointerOrientation,
                                      nsIObserver* aObserver) override;
  nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override;

  nsresult SynthesizeNativePenInput(uint32_t aPointerId,
                                    TouchPointerState aPointerState,
                                    LayoutDeviceIntPoint aPoint,
                                    double aPressure, uint32_t aRotation,
                                    int32_t aTiltX, int32_t aTiltY,
                                    int32_t aButton,
                                    nsIObserver* aObserver) override;

  /*
   * WM_APPCOMMAND common handler.
   * Sends events via NativeKey::HandleAppCommandMessage().
   */
  bool HandleAppCommandMsg(const MSG& aAppCommandMsg, LRESULT* aRetValue);

  const InputContext& InputContextRef() const { return mInputContext; }

 private:
  using TimeStamp = mozilla::TimeStamp;
  using TimeDuration = mozilla::TimeDuration;
  using TaskbarWindowPreview = mozilla::widget::TaskbarWindowPreview;
  using NativeKey = mozilla::widget::NativeKey;
  using MSGResult = mozilla::widget::MSGResult;
  using PlatformCompositorWidgetDelegate =
      mozilla::widget::PlatformCompositorWidgetDelegate;

  struct Desktop {
    // Cached GUID of the virtual desktop this window should be on.
    // This value may be stale.
    nsString mID;
    bool mUpdateIsQueued = false;
  };

  class PointerInfo {
   public:
    enum class PointerType : uint8_t {
      TOUCH,
      PEN,
    };

    PointerInfo(int32_t aPointerId, LayoutDeviceIntPoint& aPoint,
                PointerType aType)
        : mPointerId(aPointerId), mPosition(aPoint), mType(aType) {}

    int32_t mPointerId;
    LayoutDeviceIntPoint mPosition;
    PointerType mType;
  };

  class FrameState {
   public:
    explicit FrameState(nsWindow* aWindow);

    void ConsumePreXULSkeletonState(bool aWasMaximized);

    // Whether we should call ShowWindow with the relevant size mode if needed.
    // We want to avoid that when Windows is already performing the change for
    // us (via the SWP_FRAMECHANGED messages).
    enum class DoShowWindow : bool { No, Yes };

    void EnsureSizeMode(nsSizeMode, DoShowWindow = DoShowWindow::Yes);
    void EnsureFullscreenMode(bool, DoShowWindow = DoShowWindow::Yes);
    void OnFrameChanging();
    void OnFrameChanged();

    nsSizeMode GetSizeMode() const;

    void CheckInvariant() const;

   private:
    void SetSizeModeInternal(nsSizeMode, DoShowWindow);

    nsSizeMode mSizeMode = nsSizeMode_Normal;
    // XXX mLastSizeMode is rather bizarre and needs some documentation.
    nsSizeMode mLastSizeMode = nsSizeMode_Normal;
    // The old size mode before going into fullscreen mode. This should never
    // be nsSizeMode_Fullscreen.
    nsSizeMode mPreFullscreenSizeMode = nsSizeMode_Normal;
    // Whether we're in fullscreen. We need to keep this state out of band,
    // rather than just using mSizeMode, because a window can be minimized
    // while fullscreen, and we don't store the fullscreen state anywhere else.
    bool mFullscreenMode = false;
    nsWindow* mWindow;
  };

  // Manager for taskbar-hiding. No persistent state.
  class TaskbarConcealer;

  // A magic number to identify the FAKETRACKPOINTSCROLLABLE window created
  // when the trackpoint hack is enabled.
  enum { eFakeTrackPointScrollableID = 0x46545053 };

  // Used for displayport suppression during window resize
  enum ResizeState { NOT_RESIZING, IN_SIZEMOVE, RESIZING, MOVING };

  ~nsWindow() override;

  void WindowUsesOMTC() override;
  void RegisterTouchWindow() override;

  /**
   * Callbacks
   */
  static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
                                     LPARAM lParam);
  static LRESULT CALLBACK WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam,
                                             LPARAM lParam);

  static BOOL CALLBACK BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg);
  static BOOL CALLBACK BroadcastMsg(HWND aTopWindow, LPARAM aMsg);
  static BOOL CALLBACK DispatchStarvedPaints(HWND aTopWindow, LPARAM aMsg);
  static BOOL CALLBACK RegisterTouchForDescendants(HWND aTopWindow,
                                                   LPARAM aMsg);
  static BOOL CALLBACK UnregisterTouchForDescendants(HWND aTopWindow,
                                                     LPARAM aMsg);
  static LRESULT CALLBACK MozSpecialMsgFilter(int code, WPARAM wParam,
                                              LPARAM lParam);
  static LRESULT CALLBACK MozSpecialWndProc(int code, WPARAM wParam,
                                            LPARAM lParam);
  static LRESULT CALLBACK MozSpecialMouseProc(int code, WPARAM wParam,
                                              LPARAM lParam);
  static VOID CALLBACK HookTimerForPopups(HWND hwnd, UINT uMsg, UINT idEvent,
                                          DWORD dwTime);

  /**
   * Window utilities
   */
  LPARAM lParamToScreen(LPARAM lParam);
  LPARAM lParamToClient(LPARAM lParam);

  WPARAM wParamFromGlobalMouseState();

  bool AssociateWithNativeWindow();
  void DissociateFromNativeWindow();
  bool CanTakeFocus();
  bool UpdateNonClientMargins(bool aReflowWindow = true);
  void UpdateDarkModeToolbar();
  void UpdateGetWindowInfoCaptionStatus(bool aActiveCaption);
  void ResetLayout();
  void InvalidateNonClientRegion();
  HRGN ExcludeNonClientFromPaintRegion(HRGN aRegion);
  static const wchar_t* GetMainWindowClass();
  bool HasGlass() const {
    return mTransparencyMode == TransparencyMode::BorderlessGlass;
  }
  HWND GetOwnerWnd() const { return ::GetWindow(mWnd, GW_OWNER); }
  bool IsOwnerForegroundWindow() const {
    HWND owner = GetOwnerWnd();
    return owner && owner == ::GetForegroundWindow();
  }
  bool IsForegroundWindow() const { return mWnd == ::GetForegroundWindow(); }
  bool IsPopup() const { return mWindowType == WindowType::Popup; }
  bool IsCloaked() const { return mIsCloaked; }

  /**
   * Event processing helpers
   */
  HWND GetTopLevelForFocus(HWND aCurWnd);
  void DispatchFocusToTopLevelWindow(bool aIsActivate);
  bool DispatchStandardEvent(mozilla::EventMessage aMsg);
  void RelayMouseEvent(UINT aMsg, WPARAM wParam, LPARAM lParam);
  bool ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
                      LRESULT* aRetValue);
  // We wrap this in ProcessMessage so we can log the return value
  bool ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
                              LRESULT* aRetValue);
  bool ExternalHandlerProcessMessage(UINT aMessage, WPARAM& aWParam,
                                     LPARAM& aLParam, MSGResult& aResult);
  LRESULT ProcessCharMessage(const MSG& aMsg, bool* aEventDispatched);
  LRESULT ProcessKeyUpMessage(const MSG& aMsg, bool* aEventDispatched);
  LRESULT ProcessKeyDownMessage(const MSG& aMsg, bool* aEventDispatched);
  static bool EventIsInsideWindow(
      nsWindow* aWindow,
      mozilla::Maybe<POINT> aEventPoint = mozilla::Nothing());
  static void PostSleepWakeNotification(const bool aIsSleepMode);
  int32_t ClientMarginHitTestPoint(int32_t mx, int32_t my);
  void SetWindowButtonRect(WindowButtonType aButtonType,
                           const LayoutDeviceIntRect& aClientRect) override {
    mWindowBtnRect[aButtonType] = aClientRect;
  }
  TimeStamp GetMessageTimeStamp(LONG aEventTime) const;
  static void UpdateFirstEventTime(DWORD aEventTime);
  void FinishLiveResizing(ResizeState aNewState);
  mozilla::Maybe<mozilla::PanGestureInput> ConvertTouchToPanGesture(
      const mozilla::MultiTouchInput& aTouchInput, PTOUCHINPUT aOriginalEvent);
  void DispatchTouchOrPanGestureInput(mozilla::MultiTouchInput& aTouchInput,
                                      PTOUCHINPUT aOSEvent);

  /**
   * Event handlers
   */
  void OnDestroy() override;
  bool OnResize(const LayoutDeviceIntSize& aSize);
  void OnSizeModeChange();
  bool OnGesture(WPARAM wParam, LPARAM lParam);
  bool OnTouch(WPARAM wParam, LPARAM lParam);
  bool OnHotKey(WPARAM wParam, LPARAM lParam);
  bool OnPaint(HDC aDC, uint32_t aNestingLevel);
  void OnWindowPosChanging(WINDOWPOS* info);
  void OnWindowPosChanged(WINDOWPOS* wp);
  void OnSysColorChanged();
  void OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height);
  bool OnPointerEvents(UINT msg, WPARAM wParam, LPARAM lParam);

  /**
   * Function that registers when the user has been active (used for detecting
   * when the user is idle).
   */
  void UserActivity();

  int32_t GetHeight(int32_t aProposedHeight);

  DWORD WindowStyle();
  DWORD WindowExStyle();

  static const wchar_t* ChooseWindowClass(WindowType, bool aForMenupopupFrame);
  // This method registers the given window class, and returns the class name.
  static const wchar_t* RegisterWindowClass(const wchar_t* aClassName,
                                            UINT aExtraStyle, LPWSTR aIconID);

  /**
   * XP and Vista theming support for windows with rounded edges
   */
  void ClearThemeRegion();

  /**
   * Popup hooks
   */
  static void ScheduleHookTimer(HWND aWnd, UINT aMsgId);
  static void RegisterSpecialDropdownHooks();
  static void UnregisterSpecialDropdownHooks();
  static bool GetPopupsToRollup(
      nsIRollupListener* aRollupListener, uint32_t* aPopupsToRollup,
      mozilla::Maybe<POINT> aEventPoint = mozilla::Nothing());
  static bool NeedsToHandleNCActivateDelayed(HWND aWnd);
  static bool DealWithPopups(HWND inWnd, UINT inMsg, WPARAM inWParam,
                             LPARAM inLParam, LRESULT* outResult);

  /**
   * Window transparency helpers
   */
  void SetWindowTranslucencyInner(TransparencyMode aMode);
  TransparencyMode GetWindowTranslucencyInner() const {
    return mTransparencyMode;
  }
  void UpdateGlass();
  bool IsSimulatedClientArea(int32_t clientX, int32_t clientY);
  bool IsWindowButton(int32_t hitTestResult);

  bool DispatchTouchEventFromWMPointer(UINT msg, LPARAM aLParam,
                                       const WinPointerInfo& aPointerInfo,
                                       mozilla::MouseButton aButton);

  static bool IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult);
  void IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam);

  /**
   * Misc.
   */
  void StopFlashing();
  static HWND WindowAtMouse();
  static bool IsTopLevelMouseExit(HWND aWnd);
  LayoutDeviceIntRegion GetRegionToPaint(bool aForceFullRepaint, PAINTSTRUCT ps,
                                         HDC aDC);
  nsIWidgetListener* GetPaintListener();

  void AddWindowOverlayWebRenderCommands(
      mozilla::layers::WebRenderBridgeChild* aWrBridge,
      mozilla::wr::DisplayListBuilder& aBuilder,
      mozilla::wr::IpcResourceUpdateQueue& aResourceUpdates) override;

  void CreateCompositor() override;
  void DestroyCompositor() override;
  void RequestFxrOutput() override;

  void RecreateDirectManipulationIfNeeded();
  void ResizeDirectManipulationViewport();
  void DestroyDirectManipulation();

  bool NeedsToTrackWindowOcclusionState();

  void AsyncUpdateWorkspaceID(Desktop& aDesktop);

  // See bug 603793
  static bool HasBogusPopupsDropShadowOnMultiMonitor();

  static void InitMouseWheelScrollData();

  void ChangedDPI();

  static bool InitTouchInjection();

  bool InjectTouchPoint(uint32_t aId, LayoutDeviceIntPoint& aPoint,
                        POINTER_FLAGS aFlags, uint32_t aPressure = 1024,
                        uint32_t aOrientation = 90);

  void OnFullscreenChanged(nsSizeMode aOldSizeMode, bool aFullScreen);
  void TryDwmResizeHack();

  static void OnCloakEvent(HWND aWnd, bool aCloaked);
  void OnCloakChanged(bool aCloaked);

#ifdef DEBUG
  virtual nsresult SetHiDPIMode(bool aHiDPI) override;
  virtual nsresult RestoreHiDPIMode() override;
#endif

  // Get the orientation of the hidden taskbar, on the screen that this window
  // is on, or Nothing if taskbar isn't hidden.
  mozilla::Maybe<UINT> GetHiddenTaskbarEdge();

  static bool sTouchInjectInitialized;
  static InjectTouchInputPtr sInjectTouchFuncPtr;
  static uint32_t sInstanceCount;
  static nsWindow* sCurrentWindow;
  static bool sIsOleInitialized;
  static Cursor sCurrentCursor;
  static bool sJustGotDeactivate;
  static bool sJustGotActivate;
  static bool sIsInMouseCapture;
  static bool sIsRestoringSession;

  // Hook Data Members for Dropdowns. sProcessHook Tells the
  // hook methods whether they should be processing the hook
  // messages.
  static HHOOK sMsgFilterHook;
  static HHOOK sCallProcHook;
  static HHOOK sCallMouseHook;
  static bool sProcessHook;
  static UINT sRollupMsgId;
  static HWND sRollupMsgWnd;
  static UINT sHookTimerId;

  // Used to prevent dispatching mouse events that do not originate from user
  // input.
  static POINT sLastMouseMovePoint;

  nsClassHashtable<nsUint32HashKey, PointerInfo> mActivePointers;

  // This is used by SynthesizeNativeTouchPoint to maintain state between
  // multiple synthesized points, in the case where we can't call InjectTouch
  // directly.
  mozilla::UniquePtr<mozilla::MultiTouchInput> mSynthesizedTouchInput;

  InputContext mInputContext;

  nsCOMPtr<nsIWidget> mParent;
  nsIntSize mLastSize = nsIntSize(0, 0);
  nsIntPoint mLastPoint;
  HWND mWnd = nullptr;
  HWND mTransitionWnd = nullptr;
  mozilla::Maybe<WNDPROC> mPrevWndProc;
  HBRUSH mBrush;
  IMEContext mDefaultIMC;
  HDEVNOTIFY mDeviceNotifyHandle = nullptr;
  bool mIsTopWidgetWindow = false;
  bool mInDtor = false;
  bool mIsVisible = false;
  bool mIsCloaked = false;
  bool mTouchWindow = false;
  bool mDisplayPanFeedback = false;
  bool mHideChrome = false;
  bool mIsRTL;
  bool mMousePresent = false;
  bool mSimulatedClientArea = false;
  bool mDestroyCalled = false;
  bool mOpeningAnimationSuppressed;
  bool mAlwaysOnTop;
  bool mIsEarlyBlankWindow = false;
  bool mIsShowingPreXULSkeletonUI = false;
  bool mResizable = false;
  bool mForMenupopupFrame = false;
  bool mIsPerformingDwmFlushHack = false;
  DWORD_PTR mOldStyle = 0;
  DWORD_PTR mOldExStyle = 0;
  nsNativeDragTarget* mNativeDragTarget = nullptr;
  HKL mLastKeyboardLayout = 0;
  mozilla::CheckInvariantWrapper<FrameState> mFrameState;
  WindowHook mWindowHook;
  uint32_t mPickerDisplayCount = 0;
  HICON mIconSmall = nullptr;
  HICON mIconBig = nullptr;
  HWND mLastKillFocusWindow = nullptr;
  PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate = nullptr;

  LayoutDeviceIntMargin NonClientSizeMargin() const {
    return NonClientSizeMargin(mNonClientOffset);
  }
  LayoutDeviceIntMargin NonClientSizeMargin(
      const LayoutDeviceIntMargin& aNonClientOffset) const;
  LayoutDeviceIntMargin NormalWindowNonClientOffset() const;

  // Non-client margin settings
  // Pre-calculated outward offset applied to default frames
  LayoutDeviceIntMargin mNonClientOffset;
  // Margins set by the owner
  LayoutDeviceIntMargin mNonClientMargins;
  // Margins we'd like to set once chrome is reshown:
  LayoutDeviceIntMargin mFutureMarginsOnceChromeShows;
  // Indicates we need to apply margins once toggling chrome into showing:
  bool mFutureMarginsToUse = false;

  // Indicates custom frames are enabled
  bool mCustomNonClient = false;
  // Indicates custom resize margins are in effect
  bool mUseResizeMarginOverrides = false;
  // Width of the left and right portions of the resize region
  mozilla::LayoutDeviceIntCoord mHorResizeMargin;
  // Height of the top and bottom portions of the resize region
  mozilla::LayoutDeviceIntCoord mVertResizeMargin;
  // Height of the caption plus border
  mozilla::LayoutDeviceIntCoord mCaptionHeight;

  // not yet set, will be calculated on first use
  double mDefaultScale = -1.0;

  // not yet set, will be calculated on first use
  float mAspectRatio = 0.0;

  nsCOMPtr<nsIUserIdleServiceInternal> mIdleService;

  // Draggable titlebar region maintained by UpdateWindowDraggingRegion
  LayoutDeviceIntRegion mDraggableRegion;

  // Graphics
  HDC mPaintDC = nullptr;  // only set during painting

  LayoutDeviceIntRect mLastPaintBounds;

  ResizeState mResizeState = NOT_RESIZING;

  // Transparency
  TransparencyMode mTransparencyMode = TransparencyMode::Opaque;
  nsIntRegion mPossiblyTransparentRegion;
  MARGINS mGlassMargins = {0, 0, 0, 0};

  // Win7 Gesture processing and management
  nsWinGesture mGesture;

  // Weak ref to the nsITaskbarWindowPreview associated with this window
  nsWeakPtr mTaskbarPreview = nullptr;

  // The input region that determines whether mouse events should be ignored
  // and pass through to the window below. This is currently only used for
  // popups.
  InputRegion mInputRegion;

  // True if the taskbar (possibly through the tab preview) tells us that the
  // icon has been created on the taskbar.
  bool mHasTaskbarIconBeenCreated = false;

  // Whether we're in the process of sending a WM_SETTEXT ourselves
  bool mSendingSetText = false;

  // Whether we we're created as a child window (aka ChildWindow) or not.
  bool mIsChildWindow : 1;

  int32_t mCachedHitTestResult = 0;

  // The point in time at which the last paint completed. We use this to avoid
  //  painting too rapidly in response to frequent input events.
  TimeStamp mLastPaintEndTime;

  // The location of the window buttons in the window.
  mozilla::Maybe<LayoutDeviceIntRect> mWindowButtonsRect;

  // Caching for hit test results (in client coordinates)
  LayoutDeviceIntPoint mCachedHitTestPoint;
  TimeStamp mCachedHitTestTime;

  RefPtr<mozilla::widget::InProcessWinCompositorWidget> mBasicLayersSurface;

  double mSizeConstraintsScale;  // scale in effect when setting constraints

  // Will be calculated when layer manager is created.
  int32_t mMaxTextureSize = -1;

  // Pointer events processing and management
  WinPointerEvents mPointerEvents;

  ScreenPoint mLastPanGestureFocus;

  // When true, used to indicate an async call to RequestFxrOutput to the GPU
  // process after the Compositor is created
  bool mRequestFxrOutputPending = false;

  // A stack based class used in DispatchMouseEvent() to tell whether we should
  // NOT open context menu when we receives WM_CONTEXTMENU after the
  // DispatchMouseEvent calls.
  // This class now works only in the case where a mouse up event happened in
  // the overscroll gutter.
  class MOZ_STACK_CLASS ContextMenuPreventer final {
   public:
    explicit ContextMenuPreventer(nsWindow* aWindow)
        : mWindow(aWindow), mNeedsToPreventContextMenu(false){};
    ~ContextMenuPreventer() {
      mWindow->mNeedsToPreventContextMenu = mNeedsToPreventContextMenu;
    }
    void Update(const mozilla::WidgetMouseEvent& aEvent,
                const nsIWidget::ContentAndAPZEventStatus& aEventStatus);

   private:
    nsWindow* mWindow;
    bool mNeedsToPreventContextMenu = false;
  };
  friend class ContextMenuPreventer;
  bool mNeedsToPreventContextMenu = false;

  mozilla::UniquePtr<mozilla::widget::DirectManipulationOwner> mDmOwner;

  // Client rect for minimize, maximize and close buttons.
  mozilla::EnumeratedArray<WindowButtonType, WindowButtonType::Count,
                           LayoutDeviceIntRect>
      mWindowBtnRect;

  mozilla::DataMutex<Desktop> mDesktopId;

  // If set, indicates the edge of the NC region we should clear to black
  // on next paint.  One of: ABE_TOP, ABE_BOTTOM, ABE_LEFT or ABE_RIGHT.
  mozilla::Maybe<UINT> mClearNCEdge;

  friend class nsWindowGfx;

  static constexpr int kHiddenTaskbarSize = 2;
};

#endif  // WIDGET_WINDOWS_NSWINDOW_H_
